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(可重入鎖):鎖的一種變體,允許執行緒在釋放之前多次獲取相同的鎖,這在遞迴函式或巢狀函式呼叫中很有用。
  • 訊號量:類似於鎖,但帶有一個計數器。執行緒可以獲取訊號量,直到初始化時定義的某個限制。訊號量對於限制對具有固定容量的資源的訪問很有用。
  • 屏障:允許固定數量的執行緒在屏障點同步,並且只有在所有執行緒都到達該點後才能繼續執行。屏障對於協調必須全部完成某個執行階段才能繼續執行的執行緒組很有用。
廣告