Python - 线程同步



在 Python 中,当多个线程并发地操作共享资源时,同步它们的访问以维护数据完整性和程序正确性非常重要。Python 中的线程同步可以通过使用 **threading** 模块提供的各种同步原语来实现,例如锁、条件、信号量和屏障,以控制对共享资源的访问并协调多个线程的执行。

在本教程中,我们将学习 Python 的 **threading** 模块提供的各种同步原语。

使用锁进行线程同步

Python 的 threading 模块中的锁对象提供了最简单的同步原语。它们允许线程在代码的关键部分获取和释放锁,确保一次只有一个线程可以执行受保护的代码。

通过调用 **Lock()** 方法创建一个新的锁,该方法返回一个锁对象。可以使用 **acquire(blocking)** 方法获取锁,该方法强制线程同步运行。可选的 blocking 参数允许您控制线程是否等待获取锁,并使用 **release()** 方法释放锁。

示例

以下示例演示了如何在 Python 中使用锁(threading.Lock() 方法)来同步线程,确保多个线程安全正确地访问共享资源。

import threading

counter = 10

def increment(theLock, N):
   global counter
   for i in range(N):
      theLock.acquire()
      counter += 1
      theLock.release()

lock = threading.Lock()
t1 = threading.Thread(target=increment, args=[lock, 2])
t2 = threading.Thread(target=increment, args=[lock, 10])
t3 = threading.Thread(target=increment, args=[lock, 4])

t1.start()
t2.start()
t3.start()

# Wait for all threads to complete
for thread in (t1, t2, t3):
   thread.join()

print("All threads have completed")
print("The Final Counter Value:", counter)

输出

执行上述代码时,会产生以下输出:

All threads have completed
The Final Counter Value: 26

用于同步 Python 线程的条件对象

条件变量允许线程等待,直到被另一个线程通知。它们对于提供线程之间的通信非常有用。wait()方法用于阻塞一个线程,直到它被另一个线程通过notify()notify_all()通知。

示例

此示例演示了Condition对象如何使用notify()wait()方法来同步线程。

import threading

counter = 0  

# Consumer function
def consumer(cv):
   global counter
   with cv:
      print("Consumer is waiting")
      cv.wait()  # Wait until notified by increment
      print("Consumer has been notified. Current Counter value:", counter)

# increment function
def increment(cv, N):
   global counter
   with cv:
      print("increment is producing items")
      for i in range(1, N + 1):
         counter += i  # Increment counter by i
        
      # Notify the consumer 
      cv.notify()  
      print("Increment has finished")

# Create a Condition object
cv = threading.Condition()

# Create and start threads
consumer_thread = threading.Thread(target=consumer, args=[cv])
increment_thread = threading.Thread(target=increment, args=[cv, 5])

consumer_thread.start()
increment_thread.start()

consumer_thread.join()
increment_thread.join()

print("The Final Counter Value:", counter)

输出

执行上述程序后,将产生以下输出:

Consumer is waiting
increment is producing items
Increment has finished
Consumer has been notified. Current Counter value: 15
The Final Counter Value: 15

使用join()方法同步线程

Python的threading模块中的join()方法用于等待所有线程完成执行。这是一种同步主线程与其他线程完成的简单方法。

示例

这演示了使用join()方法同步线程,以确保主线程在继续执行之前等待所有启动的线程完成其工作。

import threading
import time

class MyThread(threading.Thread):
   def __init__(self, threadID, name, counter):
      threading.Thread.__init__(self)
      self.threadID = threadID
      self.name = name
      self.counter = counter
      
   def run(self):
      print("Starting " + self.name)    
      print_time(self.name, self.counter, 3)
      
def print_time(threadName, delay, counter):
   while counter:
      time.sleep(delay)
      print("%s: %s" % (threadName, time.ctime(time.time())))
      counter -= 1
      
threads = []

# Create new threads
thread1 = MyThread(1, "Thread-1", 1)
thread2 = MyThread(2, "Thread-2", 2)

# Start the new Threads
thread1.start()
thread2.start()

# Join the threads
thread1.join()
thread2.join()

print("Exiting Main Thread")

输出

执行上述程序后,将产生以下输出:

Starting Thread-1
Starting Thread-2
Thread-1: Mon Jul  1 16:05:14 2024
Thread-2: Mon Jul  1 16:05:15 2024
Thread-1: Mon Jul  1 16:05:15 2024
Thread-1: Mon Jul  1 16:05:16 2024
Thread-2: Mon Jul  1 16:05:17 2024
Thread-2: Mon Jul  1 16:05:19 2024
Exiting Main Thread

其他同步原语

除了上述同步原语之外,Python的threading模块还提供:−

  • RLocks(可重入锁):锁的一种变体,允许线程在释放之前多次获取相同的锁,这在递归函数或嵌套函数调用中很有用。
  • 信号量:类似于锁,但带有一个计数器。线程可以获取信号量,直到初始化时定义的某个限制。信号量对于限制对具有固定容量的资源的访问很有用。
  • 屏障:允许固定数量的线程在屏障点同步,并且只有在所有线程都到达该点后才能继续执行。屏障对于协调必须全部完成某个执行阶段才能继续执行的线程组很有用。
广告