1. 了解线程
1.1 进程和线程
进程是资源分配的最小单位。每个进程都有独立的地址空间,进程之间互不影响。操作系统会为每个进程分配独立的内存空间和其他资源,如文件描述符等。
线程是CPU调度的最小单位。一个进程可以包含多个线程,这些线程共享该进程的资源(如内存),但每个线程都有自己的栈空间和程序计数器。线程之间切换开销较小,适合并发执行。
2. Thread:执行线程的对象
2.1 threading.Thread构造函数
构造一个新的线程对象,其构造函数的参数如下:
group: 目前始终为 `None`,用于将来实现线程组时使用。
target: 指定线程启动时需要调用的可调用对象(函数或方法)。如果为 `None`,则什么也不调用。常用于指定线程要执行的任务。
name: 指定线程的名称。如果为 `None`,则系统自动分配一个以 "Thread-N" 开头的名字。便于调试和管理。
args: 传递给 `target` 函数的位置参数元组。用于传递参数给线程执行的函数。
kwargs: 传递给 `target` 函数的关键字参数字典。用于传递参数给线程执行的函数。
daemon: 指定线程是否为守护线程。守护线程会在主线程结束后自动结束,非守护线程则会阻止主线程的结束。守护线程常用于后台运行的任务,如日志记录器。 示例代码
import threading
def print_numbers(n):
for i in range(n):
print(f"Number: {i}")
# 创建线程
t = threading.Thread(target=print_numbers, args=(5,), name="NumberPrinter")
t.start()
t.join()
print("Thread has finished execution")
2.2 join()方法等待线程结束
join() 方法用于阻塞当前线程,直到调用 `join()` 的线程执行完毕。常用于在主线程中等待其他子线程结束后再继续执行。 示例代码
import threading
import time
def delayed_print():
time.sleep(2)
print("Delayed print")
# 创建并启动线程
t = threading.Thread(target=delayed_print)
t.start()
print("Waiting for the thread to finish...")
t.join()
print("Thread has finished execution")
3. 线程同步
3.1 线程同步的概念
线程同步用于协调多个线程对共享资源的访问,避免数据竞争和不一致性。当多个线程同时访问和修改共享资源时,可能会导致数据不一致,线程同步机制可以确保同一时间只有一个线程能够访问共享资源,从而保证数据的一致性。
3.2 锁的概念
锁(Lock)是一种用于线程同步的原语,用于确保同一时间只有一个线程能访问某个特定资源。
4. Lock 和 RLock 对象
4.1 Lock:锁原语对象
Lock` 是最简单的一种线程同步机制。它只有两个状态:locked 和 unlocked。当一个线程获取到锁时,锁的状态变为 locked,其他线程尝试获取该锁时会被阻塞,直到该锁被释放。
import threading
lock = threading.Lock()
shared_resource = 0
def increment():
global shared_resource
with lock:
local_copy = shared_resource
local_copy += 1
shared_resource = local_copy
print(f"Resource value: {shared_resource}")
threads = []
for i in range(5):
t = threading.Thread(target=increment)
threads.append(t)
t.start()
for t in threads:
t.join()
4.2 RLock:可重入锁对象
RLock(可重入锁)允许同一个线程多次获取同一个锁,每次获取锁后必须释放相同次数的锁。这样可以避免在同一线程内多次获取锁时发生死锁。
import threading
rlock = threading.RLock()
shared_resource = 0
def increment():
global shared_resource
with rlock:
with rlock:
local_copy = shared_resource
local_copy += 1
shared_resource = local_copy
print(f"Resource value: {shared_resource}")
threads = []
for i in range(5):
t = threading.Thread(target=increment)
threads.append(t)
t.start()
for t in threads:
t.join()
4.3 Lock 和 RLock 的区别
Lock: 一个线程如果多次获取同一个 `Lock`,会导致死锁。适用于简单的线程同步情况。
RLock: 允许同一个线程多次获取锁,必须释放相同次数的锁。适用于复杂的线程同步情况,需要在同一线程内多次获取锁。
5. 死锁
5.1 死锁是什么
死锁是一种情况,当两个或多个线程互相等待对方释放资源时,会导致所有线程都被无限期地阻塞。简单来说,死锁是线程在等待中无法继续执行的状态。
import threading
lock1 = threading.Lock()
lock2 = threading.Lock()
def task1():
with lock1:
print("Task 1 acquired lock 1")
with lock2:
print("Task 1 acquired lock 2")
def task2():
with lock2:
print("Task 2 acquired lock 2")
with lock1:
print("Task 2 acquired lock 1")
thread1 = threading.Thread(target=task1)
thread2 = threading.Thread(target=task2)
thread1.start()
thread2.start()
thread1.join()
thread2.join()
这个示例中,如果task1拿到lock1后等待 lock2,同时 task2 拿到 lock2 后等待 lock1,就会发生死锁。
6. 线程的其他同步机制
除了 `Lock` 和 `RLock`,Python `threading` 模块还提供了其他一些同步机制:
6.1 Semaphore:信号量
信号量用于控制对共享资源的访问数量。它维护一个计数器,每次 `acquire` 时减少计数器,每次 `release` 时增加计数器。计数器不能小于零,当计数器为零时,试图 `acquire` 的线程会被阻塞。 示例代码
import threading
import time
semaphore = threading.Semaphore(3)
def access_resource():
with semaphore:
print(f"Resource accessed by {threading.current_thread().name}")
time.sleep(1)
threads = []
for i in range(10):
t = threading.Thread(target=access_resource, name=f"Thread-{i+1}")
threads.append(t)
t.start()
for t in threads:
t.join()
6.2 Event:事件对象
事件对象用于线程间通信。一个线程等待事件发生,另一个线程触发事件。事件对象有一个内部标志,初始值为 `False`。当调用 `set()` 方法时,内部标志变为 `True`,所有等待该事件的线程被唤醒。当调用 `clear()` 方法时,内部标志变为 `False`。
import threading
import time
event = threading.Event()
def wait_for_event():
print(f"{threading.current_thread().name} waiting for event")
event.wait()
print(f"{threading.current_thread().name} event triggered")
def trigger_event():
time.sleep(2)
print("Event will be triggered")
event.set()
threads = [threading.Thread(target=wait_for_event, name=f"Thread-{i+1}") for i in range(5)]
for t in threads:
t.start()
trigger_thread = threading.Thread(target=trigger_event)
trigger_thread.start()
for t in threads:
t.join()
trigger_thread.join()
6.3 Condition:条件变量
条件变量允许线程在满足特定条件时被唤醒。它通常与锁(Lock 或 RLock)一起使用,用于复杂的线程间同步问题。
import threading
import time
condition = threading.Condition()
shared_resource = []
def producer():
global shared_resource
while True:
with condition:
if len(shared_resource) < 5:
item = len(shared_resource) + 1
shared_resource.append(item)
print(f"Produced item {item}")
condition.notify_all()
time.sleep(1)
def consumer():
global shared_resource
while True:
with condition:
while not shared_resource:
condition.wait()
item = shared_resource.pop(0)
print(f"Consumed item {item}")
time.sleep(1)
producer_thread = threading.Thread(target=producer)
consumer_thread = threading.Thread(target=consumer)
producer_thread.start()
consumer_thread.start()
producer_thread.join()
consumer_thread.join()
这些同步机制可以帮助你更好地管理多线程程序中的资源访问,确保数据一致性和线程安全。