Python 简明教程

Python - Synchronizing Threads

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

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

Thread Synchronization using Locks

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

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

Example

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

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)

Output

当执行以上代码时,它会生成以下输出 −

All threads have completed
The Final Counter Value: 26

Condition Objects for Synchronizing Python Threads

条件变量允许线程等到另一个线程通知后才继续执行。它们对于提供 communication between the threads 非常有用。 wait() 方法用于在另一个线程通过 notify()notify_all() 通知一个线程之前阻塞该线程。

Example

此示例演示了条件对象如何使用 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)

Output

在执行上述程序后,它将生成以下输出 −

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

Synchronizing threads using the join() Method

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

Example

这通过使用 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")

Output

在执行上述程序后,它将生成以下输出 −

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

Additional Synchronization Primitives

除了上述同步基元之外,Python 的线程模组还提供:

  1. RLocks (Reentrant Locks): 允许一个线程在释放它之前多次获取同一个锁的锁变体,它在递归函数或嵌套函数调用中很有用。

  2. *信号量:*与锁类似,但有一个计数器。线程可以获取信号量,直到初始化期间定义的特定限制。信号量对于限制对具有固定容量的资源的访问很有用。

  3. Barriers: 允许固定数量的线程在障碍点进行同步,并且只有当所有线程都到达该点时才继续执行。障碍对于协调必须在任何线程继续执行之前都必须完成特定执行阶段的一组线程非常有用。