2.1 模塊 threading
在 Python 中,多執行緒是同時執行多個執行緒 (threads)的一種方式, 這樣可以更有效地利用 CPU 資源,特別是對於輸入/輸出操作或其他可以併行執行的任務。
Python 多執行緒的基本概念:
執行緒 是最小的執行單位,可以在同一個進程中與其他執行緒並行運行。 同一進程中的所有執行緒共享相同的記憶體,這樣可以在執行緒之間交換資料。
進程 是在操作系統中執行的程序實例,具有自己的地址空間和資源。 與執行緒不同,進程彼此隔離,並通過進程間通信 (IPC) 交換資料。
GIL 是 Python 解釋器中的一種機制,阻止同時執行多個 Python 執行緒。 GIL 確保 Python 代碼執行的安全性,但限制了在多核處理器上多執行緒程序的性能。
重要! 請注意,由於 Global Interpreter Lock (GIL), 在 Python 中使用多執行緒可能無法為計算密集型任務顯著提高性能, 因為 GIL 阻止在多核處理器上同時執行多個 Python 執行緒。
模塊 threading
Python 中的 模塊 threading 提供了高級接口來處理執行緒。 它允許創建和管理執行緒,對其進行同步並組織它們之間的通信。 讓我們更詳細地了解該模塊的關鍵組件和功能。
模塊 threading 的主要組件
用于处理线程的实体:
-
Thread— 创建和管理线程的主要类。 -
Timer— 用于在指定时间后执行功能的计时器。 -
ThreadLocal— 允许为线程创建本地数据。
线程同步机制:
-
Lock— 用于防止对共享资源的同时访问的同步原语。 -
Condition— 用于更复杂线程同步的条件变量。 Event— 用于线程间通知的原语。-
Semaphore— 限制同时可以执行特定部分的线程数量的原语。 -
Barrier— 同步指定数量的线程,将其阻塞,直到所有线程到达屏障。
接下来我会讲3个用于处理线程的类, 对于线程同步机制,近期不会用到。
2.2 类 Thread
类 Thread 是用于创建和管理线程的主要类。它有4个主要方法:
start(): 开始线程的执行。-
join(): 当前线程暂停并等待启动的线程完成。 is_alive(): 如果线程正在执行,则返回True。-
run(): 包含将在线程中执行的代码的方法。继承自类Thread时会被重写。
一切比想象中简单多了 — 使用 Thread 类的示例。
启动简单线程
import threading
def worker():
print("Worker thread is running")
# 创建新线程
t = threading.Thread(target=worker) # 创建线程对象
t.start() # 启动线程
t.join() # 等待线程完成
print("Main thread is finished")
调用 start 方法后,函数 worker 将开始执行。或者,更准确地说,它的线程将被添加到活动线程列表中。
使用参数
import threading
def worker(number, text):
print(f"Worker {number}: {text}")
# 创建带参数的新线程
t = threading.Thread(target=worker, args=(1, "Hello"))
t.start()
t.join()
要传递参数给新线程,只需以元组形式指定并赋值给参数 args。 当函数被调用时,目标参数将自动传递。
重写 run 方法
import threading
class MyThread(threading.Thread):
def run(self):
print("Custom thread is running")
# 创建并启动线程
t = MyThread()
t.start()
t.join()
有两种方法可以指定流程开始执行新线程的功能 — 可以在创建 Thread 对象时通过参数 target 传递, 或者继承自 Thread 类并重写 run 函数。两种方法都合法且经常使用。
2.3 类 Timer
threading 模块中的 Timer 类用于在指定时间间隔后启动函数。 这个类在多线程环境中执行延迟任务时很有用。
计时器是通过要调用的函数和以秒为单位的延迟时间来创建和初始化的。
- 方法
start()开始计时器,倒计时指定的时间间隔,然后调用指定的函数。 - 方法
cancel()允许停止计时器,如果它还没有触发的话。 这样可以防止执行函数,如果计时器不再需要。
使用示例:
延迟启动功能
在这个例子中,hello 函数将在启动计时器后5秒钟被调用。
import threading
def hello():
print("Hello, world!")
# 创建计时器,将在5秒后调用hello函数
t = threading.Timer(5.0, hello)
t.start() # 启动计时器
在执行前停止计时器
在这里,计时器将在 hello 函数有机会执行之前停止,因此什么也不会打印。
import threading
def hello():
print("Hello, world!")
# 创建计时器
t = threading.Timer(5.0, hello)
t.start() # 启动计时器
# 在执行前停止计时器
t.cancel()
带参数的计时器
在这个例子中,计时器将在3秒后调用 greet 函数并传递参数 "Alice"。
import threading
def greet(name):
print(f"Hello, {name}!")
# 创建带参数的计时器
t = threading.Timer(3.0, greet, args=["Alice"])
t.start()
类 Timer 用于计划在一定时间后执行任务。 同时计时器不能保证绝对精确的执行时间,这取决于系统负载和线程调度器的工作。
2.4 类 ThreadLocal
类 ThreadLocal 用于创建具有其自身本地数据的线程。 在多线程应用程序中,这很有用,因为每个线程都应该有自己的数据版本,以避免冲突和同步问题。
使用 ThreadLocal 的每个线程都将拥有自己的独立数据副本。 存储在 ThreadLocal 对象中的数据对于每个线程都是独一无二的,不与其他线程共享。 这对于存储仅在一个线程上下文中使用的数据是非常方便的,例如在 Web 应用程序中当前的用戶或数据库的当前连接。
使用示例:
基本使用
在这个示例中,每个线程将其名称分配给本地变量 value 并输出。 value 的值对每个线程都是唯一的。
import threading
# 创建 ThreadLocal 对象
local_data = threading.local()
def process_data():
# 将值分配给线程的本地变量
local_data.value = threading.current_thread().name
# 访问线程的本地变量
print(f'Value in {threading.current_thread().name}: {local_data.value}')
threads = []
for i in range(5):
t = threading.Thread(target=process_data)
threads.append(t)
t.start()
for t in threads:
t.join()
在 Web 应用程序中存储用户数据
在这个示例中,每个线程处理它自己的用户请求。 user_data.user 的值对每个线程都是唯一的。
import threading
# 创建 ThreadLocal 对象
user_data = threading.local()
def process_request(user):
# 将值分配给线程的本地变量
user_data.user = user
handle_request()
def handle_request():
# 访问线程的本地变量
print(f'Handling request for user: {user_data.user}')
threads = []
users = ['Alice', 'Bob', 'Charlie']
for user in users:
t = threading.Thread(target=process_request, args=(user,))
threads.append(t)
t.start()
for t in threads:
t.join()
這就是 threading 庫中 3 個最有用的類。你可能會在工作中使用它們,而其他類別則不太可能。 現在每個人都開始轉向異步函數和 庫 asyncio。 這就是我們將要討論的內容,近期都會聊這個。
GO TO FULL VERSION