赞
踩
线程是一种轻量级的并行执行方式,它可以让我们在一个程序中同时执行多个任务。线程编程是多任务处理的一种特殊形式,它允许我们创建多个线程来执行不同的任务。
多线程通常是为了利用计算机的多核CPU(并行处理),来提升程序的运行速度。然而在多核CPU出现的几十年之前就出现多线程的概念了,其目的是不显式的切换任务的情况下,让CPU可以并发的处理若干个任务,提高资源利用率。
因为GIL的存在,有人说“python的多线程没有意义”。的确,由于GIL的存在,python的多线程无法像其它语言一样通过利用多核CPU提高程序运行速度,但其在处理IO瓶颈任务和处理需要低延迟的小任务时,仍然具有优势。同时,多线程也可解决协程调度的问题。
我第一次使用python线程是GUI程序,当主窗口打开一个子窗口的时候,子窗口如果有正在处理的任务,主窗口就会卡住,所以使用了线程。
Python的threading模块提供了多个类来支持多线程编程:
- Thread类:这是threading模块中最核心的类。它代表了一个线程,可以独立执行任务。创建Thread对象时,可以传递一个callable对象作为目标函数。Thread类还有一个重要的方法
start()
用于启动线程,以及join()
方法用于等待线程完成。- Lock类:用于实现线程间的互斥锁机制,确保同一时间只有一个线程能够访问特定的资源或代码段。它有两个主要的方法:
acquire()
和release()
,分别用于获取和释放锁。- RLock类:与Lock类似,但RLock允许同一个线程多次获得锁,这在递归调用时非常有用。
- Semaphore类:是一个更高级的锁机制,允许一定数量的线程同时访问资源。Semaphore维护了一个计数器,通过
acquire()
和release()
方法来增加或减少计数值。- Event类:用于实现线程间的同步,一个线程可以通过Event对象向另一个线程发送信号。Event对象有一个内部标志,可以通过
set()
和clear()
方法来设置和清除这个标志。- Condition类:类似于Event,但提供了更复杂的同步机制。它允许线程等待某个条件成立,然后才继续执行。通常与Lock或RLock一起使用。
- Barrier类:用于实现线程间的同步屏障,当所有线程都达到某个点时,它们才会被允许继续执行。
- Timer类:用于在指定的延迟后调用一个函数。它不是直接用于线程同步,但可以用于安排未来的任务。
- ThreadLocal类:提供了线程局部数据的功能,允许每个线程拥有自己独立的对象实例。
部分方法:
类 | 方法 | 描述 |
---|---|---|
Thread | start() | 开始线程执行。 |
run() | 线程要执行的方法。默认情况下是调用target 参数指定的函数,可以被子类重写。 | |
join(timeout=None) | 等待线程终止。如果设置了timeout ,则最多等待timeout 秒。 | |
is_alive() | 判断线程是否在运行。 | |
getName() | 获取线程名称。 | |
setName(name) | 设置线程名称。 | |
Lock | acquire(blocking=True, timeout=-1) | 获取锁。blocking=True 时,阻塞直到获得锁或超时;blocking=False 时,非阻塞获取锁。timeout 指定非阻塞等待时间。 |
release() | 释放锁。 | |
locked() | 检查锁的状态。 | |
RLock | acquire(blocking=True, timeout=-1) | 获取重入锁。行为类似于Lock ,但支持同一线程多次获取锁。 |
release() | 释放重入锁。 | |
locked() | 检查锁的状态。 | |
Condition | acquire(blocking=True, timeout=-1) | 获取锁,用于线程间同步。行为类似于Lock ,但用于线程间协调。 |
release() | 释放锁,用于线程间同步。 | |
wait(timeout=None) | 等待直到被通知或超时。必须在已获得锁的情况下调用。 | |
notify(n=1) | 通知等待的线程,至少通知n 个线程。 | |
notify_all() | 通知所有等待的线程。 | |
Semaphore | acquire(blocking=True, timeout=None) | 获取信号量。类似于Lock ,但允许多个线程同时访问临界区,但有一定限制。 |
release() | 释放信号量。 | |
Event | set() | 设置事件标志为True,通知等待该事件的所有线程。 |
clear() | 设置事件标志为False。 | |
is_set() | 检查事件标志是否为True。 | |
Timer | start() | 开始计时器线程。在指定时间后调用指定函数。 |
cancel() | 取消计时器。如果计时器仍在等待运行,则取消。 |
import threading def my_function(): print("Thread {} is running...".format(threading.Thread.getName(my_thread))) # 创建线程 my_thread = threading.Thread(target=my_function, name="myThread") # 启动线程 my_thread.start() # 等待线程执行完成 my_thread.join() print("Main thread ends.")
Python中线程同步的方法有以下几种:
acquire()
方法加锁和release()
方法解锁来实现线程间的互斥。wait()
方法等待事件发生,通过set()
方法来通知事件已发生。它们的特点和适用场景:
工具 | 特点 | 适用场景 |
---|---|---|
Lock | 最基本的互斥锁,一次只允许一个线程访问共享资源 不可重入,即同一线程再次获取会导致死锁 | 简单的线程同步需求 需要确保一段代码同一时间只能被一个线程执行 |
RLock | 可重入锁,同一线程可以多次获取锁并释放 允许同一线程多次调用 acquire() | 复杂的递归线程同步需求 某些情况下需要允许同一线程多次获取和释放锁 |
Semaphore | 允许一定数量的线程同时访问共享资源 控制并发数量 | 有限资源的并发控制 控制同时运行的线程数量,比如限流 |
Event | 可以通过 set() 和 clear() 设置和清除事件状态 线程可以等待事件的发生 | 线程间通信和同步 一个线程等待某个事件的发生,另一个线程触发事件 |
Condition | 提供了更高级的线程同步机制,结合了锁和事件 | 复杂的线程协调和通信需求 - 允许线程等待某个条件,其他线程在满足条件时通知等待的线程继续执行 |
threading.Lock
类提供了最基本的线程同步机制,它可以确保一次只有一个线程可以访问共享资源。acquire()
方法用于获取锁,release()
方法用于释放锁。import threading import time # 创建一个锁对象 lock = threading.Lock() def worker(): # 获取锁 lock.acquire() try: # 执行需要同步的操作 print("Thread {} is working...".format(threading.Thread.getName(t))) # 模拟耗时操作 time.sleep(1) finally: # 释放锁 lock.release() # 创建多个线程并启动它们 threads = [] for i in range(5): t = threading.Thread(target=worker) threads.append(t) t.start() # 等待所有线程完成 for t in threads: t.join() print("All threads finished.")
threading.RLock
是一个可重入锁,允许同一线程多次获得锁。acquire()
和 release()
方法的使用方式与 Lock
类似,但允许同一线程多次调用 acquire()
。import threading import time class Counter: def __init__(self): self.value = 0 self.lock = threading.RLock() def increment(self): with self.lock: self.value += 1 print(f"Incremented to {self.value} by {threading.currentThread().getName()}") time.sleep(0.1) # 模拟一些计算或I/O操作 def decrement(self): with self.lock: self.value -= 1 print(f"Decremented to {self.value} by {threading.currentThread().getName()}") time.sleep(0.1) # 模拟一些计算或I/O操作 def worker(counter): for _ in range(3): counter.increment() counter.decrement() # 创建 Counter 实例 counter = Counter() # 创建多个线程 threads = [] for i in range(3): thread = threading.Thread(target=worker, args=(counter,)) threads.append(thread) thread.start() # 等待线程执行完成 for thread in threads: thread.join() print("Final counter value:", counter.value)
threading.Condition
是一个高级的线程同步工具,同时提供了锁和条件等待/通知机制。acquire()
和 release()
方法用于加锁和解锁,wait()
方法用于等待条件的通知,notify()
和 notify_all()
方法用于发送通知。import threading shared_resource = [] condition = threading.Condition() def consumer(): with condition: print("Consumer waiting...") condition.wait() print("Consumer consumed the resource:", shared_resource.pop(0)) def producer(): with condition: print("Producer producing resource...") shared_resource.append("New Resource") condition.notify() print("Producer notified the consumer.") # 创建线程 consumer_thread = threading.Thread(target=consumer) producer_thread = threading.Thread(target=producer) # 启动线程 consumer_thread.start() producer_thread.start() # 等待线程执行完成 consumer_thread.join() producer_thread.join() print("Main thread ends.")
threading.Semaphore
是一种控制并发访问的计数器,它允许多个线程同时访问共享资源,但可以限制同时访问的线程数量。acquire()
和 release()
方法用于获取和释放信号量。import threading semaphore = threading.Semaphore(value=2) # 允许同时两个线程访问 def access_resource(): with semaphore: print(threading.currentThread().getName(), "is accessing the resource.") # 假设这里是对共享资源的访问 # 创建多个线程 threads = [] for i in range(5): thread = threading.Thread(target=access_resource) threads.append(thread) thread.start() # 等待线程执行完成 for thread in threads: thread.join() print("Main thread ends.")
这些是 Python 中常用的线程同步方法。选择合适的方法取决于你的应用场景,例如是否需要多次获取锁、是否需要等待条件、是否需要限制并发数量等。这些同步工具能够有效地管理多线程程序中的竞态条件,确保线程安全地访问共享资源。
Python的全局解释器锁(Global Interpreter Lock
,简称GIL)是CPython解释器中的一种线程同步机制。具体如下:
在cpython
的PR中可以看到前几天的一条PR,即添加GIL的开关。
当然不是release版,能用到可能还需要很久。
有时候,限制程序性能的可能不是GIL,而是程序的生产者。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。