iT邦幫忙

2024 iThome 鐵人賽

DAY 25
0

Python 多線程(Multithreading)是一種允許程序並發執行多個線程的技術,每個線程可以獨立完成任務。這在需要執行 I/O 密集型任務(如文件讀寫、網絡請求等)時非常有用,因為線程可以在等待外部資源的同時執行其他操作。

Python 提供了內置的 threading 模塊來支持多線程操作,這使得可以在程序中同時運行多個任務。

壹、threading 模塊的基本用法

threading 模塊允許你創建並管理線程。常見的操作有:

  • 創建並啟動線程
  • 使用線程執行任務
  • 等待線程完成
  • 設置線程的名稱等

一、創建和啟動線程

可以通過 threading.Thread() 來創建一個線程,並通過 start() 方法來啟動它。

import threading

def task():
    print("這是一個子線程的任務")

# 創建線程
thread = threading.Thread(target=task)

# 啟動線程
thread.start()

# 等待線程完成
thread.join()

print("主線程結束")

輸出:

這是一個子線程的任務
主線程結束
  • target:指定線程要執行的函數。
  • start():啟動線程,這將使線程進入就緒狀態,等待 CPU 調度。
  • join():讓主線程等待子線程完成後再繼續執行。

二、線程任務的傳遞參數

當線程執行的任務需要參數時,可以使用 args 參數來傳遞。

import threading

def task(name, delay):
    print(f"{name} 線程啟動,延遲 {delay} 秒")

# 創建多個線程
thread1 = threading.Thread(target=task, args=("Thread-1", 2))
thread2 = threading.Thread(target=task, args=("Thread-2", 3))

# 啟動線程
thread1.start()
thread2.start()

# 等待線程完成
thread1.join()
thread2.join()

print("主線程結束")

輸出:

Thread-1 線程啟動,延遲 2 秒
Thread-2 線程啟動,延遲 3 秒
主線程結束

貳、繼承 Thread 類來創建線程

除了使用 target 來指定線程執行的函數外,也可以通過繼承 threading.Thread 類來創建線程,這樣可以更加靈活地管理線程的行為。

一、 使用繼承來創建線程

import threading

class MyThread(threading.Thread):
    def __init__(self, name, delay):
        super().__init__()
        self.name = name
        self.delay = delay

    def run(self):
        print(f"{self.name} 線程啟動,延遲 {self.delay} 秒")

# 創建線程對象
thread1 = MyThread("Thread-1", 2)
thread2 = MyThread("Thread-2", 3)

# 啟動線程
thread1.start()
thread2.start()

# 等待線程完成
thread1.join()
thread2.join()

print("主線程結束")

輸出:

Thread-1 線程啟動,延遲 2 秒
Thread-2 線程啟動,延遲 3 秒
主線程結束

在這個例子中,我們通過繼承 threading.Thread 類來創建自己的線程類,並重寫了 run() 方法,當線程啟動時,會自動執行 run() 內的邏輯。

參、線程的同步

在多線程編程中,當多個線程需要同時讀寫共享數據時,可能會導致競爭條件(Race Condition)問題。為了防止多個線程同時訪問或修改共享資源,通常會使用「鎖」(Lock)進行同步。

一、使用 Lock 同步線程

threading.Lock 是 Python 中用來實現線程同步的基本工具。鎖保證了在同一時刻,只有一個線程可以訪問共享資源。

import threading

lock = threading.Lock()
shared_resource = 0

def task():
    global shared_resource
    for _ in range(1000000):
        with lock:  # 獲取鎖
            shared_resource += 1

# 創建多個線程
threads = []
for i in range(5):
    thread = threading.Thread(target=task)
    threads.append(thread)
    thread.start()

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

print(f"共享資源的最終值: {shared_resource}")

在這個例子中,我們使用 lock 來保證每次只有一個線程能修改 shared_resource。這避免了競爭條件導致的錯誤數據。

二、使用 RLock 來實現可重入鎖

有時候,鎖需要在同一個線程中被多次獲取,此時可以使用 threading.RLock,它是一種可重入鎖。

import threading

rlock = threading.RLock()

def task():
    with rlock:
        print("第一次獲取鎖")
        with rlock:
            print("第二次獲取鎖")

thread = threading.Thread(target=task)
thread.start()
thread.join()

輸出:

第一次獲取鎖
第二次獲取鎖

肆、其他常用的同步工具

一、Semaphore

Semaphore 允許一定數量的線程同時訪問共享資源,而不是只有一個。這在控制併發數量時非常有用。

import threading
import time

semaphore = threading.Semaphore(3)

def task(name):
    with semaphore:
        print(f"{name} 獲取了資源")
        time.sleep(2)
        print(f"{name} 釋放了資源")

# 創建多個線程
threads = []
for i in range(5):
    thread = threading.Thread(target=task, args=(f"Thread-{i+1}",))
    threads.append(thread)
    thread.start()

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

這裡的 Semaphore(3) 限制最多只能有 3 個線程同時獲取資源。

二、 Event

Event 是一個用來實現線程間通信的同步工具,線程可以等待某個事件發生。

import threading
import time

event = threading.Event()

def task():
    print("線程等待事件觸發")
    event.wait()  # 等待事件觸發
    print("事件已觸發,繼續執行")

thread = threading.Thread(target=task)
thread.start()

time.sleep(2)
print("觸發事件")
event.set()  # 觸發事件

輸出:

線程等待事件觸發
觸發事件
事件已觸發,繼續執行

上一篇
第24天:正則表達式 part 2
下一篇
第26天:網絡請求 part1
系列文
python30天入門學習30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言