多线程
简介
多线程是一种并发编程的技术,允许程序同时执行多个任务或操作。线程是操作系统调度的基本单位,而进程是资源分配的基本单位,一个进程可以包含多个线程。
并发执行是指两个或多个任务或操作在同一时间段内交替执行,并行执行是指两个或多个任务或操作在同一时刻同时执行。如果是多核CPU,则可以实现真正的并行执行。
多线程的优点:
- 可以同时执行多个任务,提高程序的运行效率。
- 可以及时响应用户的请求,通过使用单独的线程来处理用户请求,不会因为一个线程的阻塞而影响其他线程的运行。
- 与多进程相比,线程的创建和切换开销小,可以节省系统资源。
多线程的缺点:
- 线程之间共享数据时,需要进行同步,否则会导致数据错乱。
- 线程的执行顺序无法预测,因而可能导致程序的行为不一致。
- 线程的调试和维护都相对复杂。
threading模块
Python的threading
模块提供了对多线程的支持。它提供了Thread类来代表线程对象,提供了Lock、RLock、Condition、Semaphore、Event等类来提供对线程同步的支持。
创建线程
使用Thread
类可以创建线程对象,并指定线程的运行函数。
import threading
def worker():
print('Worker')
t = threading.Thread(target=worker)
t.start()
Thread
类的构造函数可以接收以下参数:
group
:线程组,默认值为None。target
:线程运行函数。name
:线程名称,默认值为None。args
:传递给线程函数的参数,默认值为None。kwargs
:传递给线程函数的关键字参数,默认值为None。
start()
方法启动线程。
线程同步
多线程编程中,线程同步是指多个线程之间需要共享某些资源,因此需要对共享资源进行保护,以避免数据竞争和线程安全问题。
Python的threading
模块提供了Lock、RLock、Condition、Semaphore、Event等类来提供对线程同步的支持。
Lock
Lock
类是互斥锁,用于在同一时刻只允许一个线程对共享资源进行访问。
import threading
counter = 0
lock = threading.Lock()
def increment():
global counter
# 获取锁
lock.acquire()
try:
# 访问共享资源
counter += 1
finally:
# 释放锁
lock.release()
threads = []
for _ in range(10):
thread = threading.Thread(target=increment)
thread.start()
threads.append(thread)
for thread in threads:
thread.join() # 等待所有线程结束
print("Counter:", counter) # 10
acquire()
方法获取锁,如果锁已经被其他线程获取,则当前线程会被阻塞,直到锁被释放。release()
方法释放锁。
RLock
RLock
类是可重入锁,它可以对共享资源进行递归锁定,即同一线程可以对同一资源进行多次加锁。与普通的Lock
相比,RLock
允许同一个线程多次获取锁,而普通的Lock
不允许同一个线程在没有释放锁的情况下再次获取锁(会导致死锁)。
import threading
rlock = threading.RLock()
def foo():
# with语句会自动获取锁,并在退出with语句时自动释放锁
with rlock:
bar() # 在同一线程内可以嵌套使用RLock
def bar():
with rlock:
print("Hello from bar")
# 创建线程
thread = threading.Thread(target=foo)
thread.start()
thread.join()
Condition
Condition
是基于Lock
的。Condition
提供了一个更高级别的线程同步原语,允许线程等待某些条件的发生,然后再继续执行。
Condition
结合了Lock
,Event
和Queue
的功能。它提供了wait()
、notify()
和notify_all()
等方法来实现线程之间的协作。
生产者-消费者模型:
import threading
import time
buffer = [] # 共享资源
buffer_size = 5 # 缓冲区大小
condition = threading.Condition() # 条件变量
# 生产者
def producer():
global buffer
for i in range(10):
with condition:
while len(buffer) >= buffer_size:
print("Buffer is full, producer is waiting")
condition.wait()
buffer.append(i)
print(f"Produced {i}")
condition.notify()
# 消费者
def consumer():
global buffer
while True:
with condition:
while len(buffer) == 0:
print("Buffer is empty, consumer is waiting")
condition.wait()
item = buffer.pop(0)
print(f"Consumed {item}")
condition.notify()
# 创建生产者和消费者线程
producer_thread = threading.Thread(target=producer)
consumer_thread = threading.Thread(target=consumer)
# 启动线程
producer_thread.start()
consumer_thread.start()
# 等待线程结束
producer_thread.join()
consumer_thread.join()
print("All threads finished")
运行结果:
wait()
方法使线程阻塞,直到其他线程调用notify()
或notifyAll()
方法唤醒它。notify()
方法唤醒一个线程,notifyAll()
方法唤醒所有等待线程。
Semaphore
Semaphore
类是信号量,用于控制对共享资源的访问数量。
同样可以使用Semaphore
类来实现生产者-消费者模型:
import threading
import time
buffer = [] # 共享资源
buffer_size = 5 # 缓冲区大小
empty = threading.Semaphore(buffer_size) # 空闲缓冲区空间的信号量
full = threading.Semaphore(0) # 已填充缓冲区空间的信号量
mutex = threading.Lock() # 互斥锁,用于保护对缓冲区的访问
# 生产者
def producer():
global buffer
for i in range(10):
empty.acquire() # 获取一个空闲缓冲区空间
mutex.acquire() # 获取对缓冲区的访问权
buffer.append(i)
print(f"Produced {i}")
mutex.release() # 释放对缓冲区的访问权
full.release() # 增加已填充缓冲区空间的信号量
# 消费者
def consumer():
global buffer
while True:
full.acquire() # 获取一个已填充的缓冲区空间
mutex.acquire() # 获取对缓冲区的访问权
item = buffer.pop(0)
print(f"Consumed {item}")
mutex.release() # 释放对缓冲区的访问权
empty.release() # 增加空闲缓冲区空间的信号量
# 创建生产者和消费者线程
producer_thread = threading.Thread(target=producer)
consumer_thread = threading.Thread(target=consumer)
# 启动线程
producer_thread.start()
consumer_thread.start()
# 等待线程结束
producer_thread.join()
consumer_thread.join()
print("All threads finished")
acquire()
方法获取信号量,如果信号量已经被其他线程获取,则当前线程会被阻塞,直到信号量被释放。release()
方法释放信号量。
Event
Event
类是事件,用于线程间通信,Event
允许一个或多个线程等待某个事件的发生。它通常用于线程间的通信和协调,典型的用法包括线程等待某个条件的触发,或者多个线程在某个事件上等待。
import threading
import time
# 事件对象
event = threading.Event()
# 线程函数,等待事件发生后打印消息
def wait_for_event():
print("Thread waiting for event...")
event.wait() # 等待事件发生
print("Thread event happened!")
# 线程函数,设置事件
def set_event():
time.sleep(2) # 等待2秒钟
print("Setting event...")
event.set() # 设置事件
# 创建并启动线程
thread1 = threading.Thread(target=wait_for_event)
thread2 = threading.Thread(target=set_event)
thread1.start()
thread2.start()
# 等待线程结束
thread1.join()
thread2.join()
print("Main thread exiting.")
结果:
wait()
方法使线程阻塞,直到其他线程调用set()
方法设置事件。set()
方法设置事件,使线程阻塞的wait()
方法解除阻塞。
Queue
Queue
可以用来在线程间传递数据。
模拟一个计算任务队列:
import threading
import queue
import time
# 定义一个队列用于存放计算任务
task_queue = queue.Queue()
# 生产者线程,负责生成计算任务
def producer():
for i in range(10):
task_queue.put((i, i+1)) # 添加任务,这里模拟计算 (i, i+1)
print(f"Produced task {i}") # 输出生产任务的信息
time.sleep(0.5)
# 所有任务添加完毕后,向队列中放入None作为结束信号
task_queue.put(None)
# 消费者线程,负责处理计算任务
def consumer():
while True:
task = task_queue.get()
if task is None:
task_queue.task_done()
break
result = task[0] + task[1] # 计算结果
print(f"Task {task} => Result {result}") # 输出计算结果的信息
task_queue.task_done()
# 创建并启动生产者线程和消费者线程
producer_thread = threading.Thread(target=producer)
consumer_thread = threading.Thread(target=consumer)
producer_thread.start()
consumer_thread.start()
# 等待生产者线程完成
producer_thread.join()
# 等待所有任务完成
task_queue.join()
print("All tasks are completed.")
put()
方法向队列中放入数据,get()
方法从队列中获取数据。
结果:
线程的状态
Thread
类使用is_alive()
判断线程是否存活。
import threading
def worker():
print('Worker')
t = threading.Thread(target=worker)
t.start()
print(t.ident)