Python - 線程同步

你好,未來的 Python 魔法師們!今天,我們將進入一個令人興奮的旅程,探索線程同步的世界。想象一下,你正在指揮一個交響樂團,每個樂手都是一個線程,你需要確保他們都能和諧地演奏。這基本上就是程序設計中線程同步的全部內容!

Python - Synchronizing Threads

使用鎖進行線程同步

讓我們從我們同步工具箱中最基本的工具開始:鎖。將鎖看作是酒店房門上的「請勿打擾」標誌。當一個線程獲取到鎖時,就像是挂上那個標誌,告訴其他線程,「嘿,我在這裡很忙!」

以下是一個簡單的例子來說明這個概念:

import threading
import time

# 共享資源
counter = 0

# 創建一個鎖
lock = threading.Lock()

def increment():
global counter
for _ in range(100000):
lock.acquire()
counter += 1
lock.release()

# 創建兩個線程
thread1 = threading.Thread(target=increment)
thread2 = threading.Thread(target=increment)

# 開始線程
thread1.start()
thread2.start()

# 等待線程結束
thread1.join()
thread2.join()

print(f"最終計數器值:{counter}")

在這個例子中,我們有一個共享資源 counter,兩個線程試圖增加它的值。如果沒有鎖,我們可能會遇到競爭條件,兩個線程試圖同時增加計數器,這可能導致不正確的結果。

通過在修改計數器之前使用 lock.acquire() 並在之後使用 lock.release(),我們確保一次只有一個線程可以增加計數器的值。這就像是接力賽跑中的傳遞接力棒——只有持有接力棒(鎖)的線程可以奔跑(修改共享資源)。

使用條件對象同步 Python 線程

現在,讓我們用條件對象來提升我們的同步遊戲。這些就像是我們線程的複雜交通信號燈,允許更複雜的協調。

以下是一個使用條件對象的生產者-消費者情景示例:

import threading
import time
import random

# 共享緩衝區
buffer = []
MAX_SIZE = 5

# 創建一個條件對象
condition = threading.Condition()

def producer():
global buffer
while True:
with condition:
while len(buffer) == MAX_SIZE:
print("緩衝區已滿,生產者在等待...")
condition.wait()
item = random.randint(1, 100)
buffer.append(item)
print(f"生產了:{item}")
condition.notify()
time.sleep(random.random())

def consumer():
global buffer
while True:
with condition:
while len(buffer) == 0:
print("緩衝區為空,消費者在等待...")
condition.wait()
item = buffer.pop(0)
print(f"消費了:{item}")
condition.notify()
time.sleep(random.random())

# 創建生產者和消費者線程
producer_thread = threading.Thread(target=producer)
consumer_thread = threading.Thread(target=consumer)

# 開始線程
producer_thread.start()
consumer_thread.start()

# 运行一段时间
time.sleep(10)

在這個例子中,我們有一個生產者向緩衝區添加項目,一個消費者從緩衝區刪除項目。條件對象幫助協調他們的動作:

  • 生產者在緩衝區滿時等待。
  • 消費者在緩衝區為空時等待。
  • 他們互相通知何時可以繼續。

這就像是一個編排得很好的舞蹈,條件對象就是編舞者!

使用 join() 方法同步線程

join() 方法就像是一個線程告訴另一個線程在登台之前等待其表演完成。這是一種簡單但強大的同步線程的方式。

以下是一個例子:

import threading
import time

def worker(name, delay):
print(f"{name} 開始...")
time.sleep(delay)
print(f"{name} 結束!")

# 創建線程
thread1 = threading.Thread(target=worker, args=("線程 1", 2))
thread2 = threading.Thread(target=worker, args=("線程 2", 4))

# 開始線程
thread1.start()
thread2.start()

# 等待線程1完成
thread1.join()
print("主線程在線程1之後等待")

# 等待線程2完成
thread2.join()
print("主線程在線程2之後等待")

print("所有線程已經完成!")

在這個例子中,主線程啟動兩個工作線程,然後使用 join() 等待它們每個完成。這就像一個父親等待他的孩子們完成作業後才端上晚餐!

還有其他的同步原语

Python 提供了許多其他用於線程同步的工具。讓我們快速查看其中的一些:

原语 描述 使用案例
信号量 允許有限數量的線程訪問資源 管理數據庫連接池
事件 允許一個線程向其他線程發出事件信號 記号一個任務已完成
屏障 允許多個線程等待所有達到某個點 同步賽跑的開始

以下是一個使用信号量的快速示例:

import threading
import time

# 創建一個允許 2 個線程同時訪問的信号量
semaphore = threading.Semaphore(2)

def worker(name):
with semaphore:
print(f"{name} 獲得了信号量")
time.sleep(1)
print(f"{name} 釋放了信号量")

# 創建並啟動 5 個線程
threads = []
for i in range(5):
thread = threading.Thread(target=worker, args=(f"線程 {i}",))
threads.append(thread)
thread.start()

# 等待所有線程完成
for thread in threads:
thread.join()

print("所有線程已經完成!")

在這個例子中,信号量就像俱樂部的保鏢,一次只允許兩個線程進入。這在需要限制訪問稀缺資源的情況下非常完美!

就是這樣,各位!我們已經探索了 Python 中線程同步的奇妙世界。請記住,就像指揮交響樂團或編排舞蹈一樣,線程同步都是關於協调和時機。有了這些工具,您就可以創建有和諧、多線程的 Python 程序了。堅持練習,保持好奇心,編程愉快!

Credits: Image by storyset