Python - 線程間通訊

大家好,未來的 Python 巫師們!今天,我們將開啟一段激動人心的旅程,進入 Python 的線程間通訊的世界。如果你是编程新手,不用擔心——我將成為你的友好嚮導,一步一步解釋一切。那麼,讓我們開始吧!

Python - Inter-thread Communication

什麼是線程間通訊?

在我們深入細節之前,先來了解線程間通訊到底是關於什麼的。想象一下,你在一個團隊中工作,負責一個大項目。大家都在做不同的部分,但有时候你需要分享信息或協調工作。程式中的線程也是這樣,線程間通訊就是他們彼此之間「交流」的方式。

事件對象

讓我們從最簡單的線程通訊方式開始:事件對象。

事件對象是什麼?

事件對象就像一個可以設置或清除的旗標。線程可以等待這個旗標被設置後再繼續執行。這有點像在過馬路之前等待綠燈。

如何使用事件對象

讓我們看一個簡單的例子:

import threading
import time

def waiter(event):
print("等待者:我正在等待事件被設置...")
event.wait()
print("等待者:事件已經設置!我可以繼續了。")

def setter(event):
print("設置者:我將在3秒後設置事件...")
time.sleep(3)
event.set()
print("設置者:我已經設置了事件!")

# 創建一個事件對象
e = threading.Event()

# 創建並啟動線程
t1 = threading.Thread(target=waiter, args=(e,))
t2 = threading.Thread(target=setter, args=(e,))

t1.start()
t2.start()

# 等待兩個線程結束
t1.join()
t2.join()

print("主線程:全部完成!")

讓我們來分解一下:

  1. 我們導入 threading 模塊來創建線程,並導入 time 模塊來添加延時。
  2. 我們定義兩個函數:waitersetter
  3. waiter 函數使用 event.wait() 等待事件被設置。
  4. setter 函數等待3秒(模擬一些工作)然後使用 event.set() 設置事件。
  5. 我們創建一個事件對象 e
  6. 我們創建兩個線程,每個函數一個,將事件對象傳遞給兩者。
  7. 我們啟動兩個線程,然後使用 join() 等待它們結束。

當你運行這個程序時,你會看到等待者在等待,然後3秒後,設置者設置事件,等待者繼續執行。

條件對象

現在,讓我們提升一個層次,看看條件對象。它就像是事件對象的更複雜的親戚。

條件對象是什麼?

條件對象允許線程等待某些條件成為真。這就像是在派對上等待一個特定的人到達後才開始遊戲。

如何使用條件對象

以下是使用條件對象的例子:

import threading
import time
import random

# 共享資源
items = []
condition = threading.Condition()

def producer():
global items
for i in range(5):
time.sleep(random.random())  # 模擬不同的生產時間
with condition:
items.append(f"項目 {i}")
print(f"生產者添加了項目 {i}")
condition.notify()  # 通知消費者有一個項目可用

def consumer():
global items
while True:
with condition:
while not items:
print("消費者在等待...")
condition.wait()
item = items.pop(0)
print(f"消費者移除了 {item}")
time.sleep(random.random())  # 模擬不同的消費時間

# 創建並啟動線程
producer_thread = threading.Thread(target=producer)
consumer_thread = threading.Thread(target=consumer)

producer_thread.start()
consumer_thread.start()

# 等待生產者結束
producer_thread.join()

# 停止消費者(它在無限循環中)
consumer_thread.daemon = True

讓我們來分解一下:

  1. 我們創建一個共享資源 items 和一個條件對象。
  2. producer 函數添加項目到清單中並通知消費者。
  3. consumer 函數等待項目可用,然後移除並「消費」它們。
  4. 我們使用 with condition: 來自動獲取和釋放條件的鎖。
  5. condition.wait() 釋放鎖並等待通知。
  6. condition.notify() 唤醒一個等待的線程。

這個例子演示了一個典型的生產者-消費者情境,其中一個線程生成項目,另一個線程消費項目。

事件對象和條件對象的比較

以下是事件對象和條件對象的快速比較:

特性 事件 條件
目的 簡單的信號發送 複雜的同步
狀態 二元(設置/清除) 可以有多種狀態
等待 線程等待事件被設置 線程等待特定的條件
通知 所有等待的線程都被通知 可以通知一個或所有等待的線程
使用案例 簡單的「去/不去」情境 生產者-消費者問題,複雜的同步

結論

恭喜你!你剛剛走出了 Python 線程間通訊的第一步。我們已經介紹了用於簡單信號發送的事件對象和用於更複雜同步情境的條件對象。

記住,就像學習任何新語言一樣,練習成就完美。嘗試使用這些對象編寫你自己的程序。也許創建一個簡單的聊天系統,其中線程代表不同的用戶,或者使用事件模擬交通信號燈系統。

線程間通訊一開始可能會覺得複雜,但隨著時間和練習,你將能夠像指揮家指揮交響樂一樣指揮線程。繼續編程,繼續學習,最重要的是,玩得開心!

Credits: Image by storyset