文章目录
- Python多线程入门
- 1. Python多线程概述
- 2. `threading`模块基础
- - Thread 类: 这是一个代表线程的类。可以通过创建`Thread`类的实例来新建一个线程。
- - Lock 类: 在多线程环境中,为了防止数据错乱,通常需要用到锁机制。`Lock`类提供了基本的锁功能,可以帮助同步线程。
- - Condition 类: 这是一种更高级的线程同步机制,它在某些条件下阻塞线程,直到被其他线程唤醒。
- 3. 创建线程
- 4. 线程间通信
- 5. 线程同步
- Python Threading模块各类详解
- Thread 类
- - target:指定线程启动时要执行的函数。
- - args:是一个元组,表示传递给目标函数的参数。
- - kwargs:是一个字典,表示传递给目标函数的关键字参数。
- - name:为线程指定一个名称,这在调试时很有用。
- - daemon:布尔值,指示线程是否是守护线程(守护线程会随着主线程的退出而退出)。
- 参数
- 守护线程的特性
- 示例(守护线程与非守护线程)
- - 守护线程(主程序退出守护线程将会被强制退出):
- - 非守护进程(主程序退出,非守护线程照样执行,直到它自己运行完)
- 示例:
- Lock 类
- - acquire(blocking=True, timeout=-1):尝试获得锁。`blocking`参数为`False`时,方法会立即返回一个布尔值,表示是否获取到了锁。`timeout`指定等待锁的最长时间。
- - release():释放锁。
- 示例:
- Semaphore 类
- - acquire(blocking=True, timeout=-1):同`Lock`的`acquire`。
- - release():释放信号量。
- 示例:
- Event 类
- - set():将事件状态设置为真。
- - clear():将事件状态设置为假。
- - wait(timeout=None):阻塞线程,直到事件的状态为真或超时。
- 示例:
- Condition 类
- - acquire() / release():与Lock相同,用于获取和释放底层锁。
- - wait(timeout=None):调用线程等待,直到被notify()或notify_all()唤醒或超时。
- - notify(n=1):唤醒一个或多个正在等待这个条件的线程。`n` 指定要唤醒的线程数。
- - notify_all():唤醒所有等待这个条件的线程。
- 示例:
- Barrier 类
- - parties:这是Barrier类构造函数的主要参数,代表需要到达屏障点的线程数。只有当指定数量的线程都已调用了wait()方法,所有这些线程才会同时被释放继续执行。这个数字必须在创建Barrier时确定,并在其生命周期内保持不变。
- - wait():阻塞调用此方法的线程,直到所有线程都已调用此方法。然后所有线程同时释放。
- 示例:
- Timer 类
- - interval:定时器激活之前的延迟时间(秒)。
- - function:定时器激活时要执行的函数。
- - args / kwargs:传递给 function 的参数。
- 示例:
- 参考文章
Python多线程入门
1. Python多线程概述
Python多线程是一种允许程序同时执行多个任务的技术。它主要通过threading
模块实现,该模块提供了丰富的API来创建和管理线程。使用多线程可以提高程序的执行效率,尤其是在执行多个独立的任务时,或者在进行大量的I/O操作时。
2. threading
模块基础
threading
模块是Python中用于实现多线程的标准库之一。这个模块中包含了多个类和函数,可以帮助开发者创建和管理线程。
- Thread 类: 这是一个代表线程的类。可以通过创建Thread
类的实例来新建一个线程。
- Lock 类: 在多线程环境中,为了防止数据错乱,通常需要用到锁机制。Lock
类提供了基本的锁功能,可以帮助同步线程。
- Condition 类: 这是一种更高级的线程同步机制,它在某些条件下阻塞线程,直到被其他线程唤醒。
3. 创建线程
创建线程的基本方法是实例化Thread
类,它接受一个目标函数和函数的参数。下面是一个简单的示例:
import threadingdef print_numbers(n):for i in range(1, n + 1):print(i)# 创建线程
thread = threading.Thread(target=print_numbers, args=(10,))
# 启动线程
thread.start()
# 等待线程完成
thread.join()
这段代码定义了一个打印数字的函数,并在一个新线程中运行这个函数。
4. 线程间通信
线程间的通信可以通过全局变量、队列等方式实现。Python的queue.Queue
类是线程安全的,非常适合用来进行线程间的数据传递。
import threading
import queue# 创建一个队列
q = queue.Queue()def producer():for i in range(10):q.put(i)print(f"Produced {i}")def consumer():while True:item = q.get()if item is None:breakprint(f"Consumed {item}")q.task_done()# 创建生产者和消费者线程
t1 = threading.Thread(target=producer)
t2 = threading.Thread(target=consumer)t1.start()
t2.start()t1.join()
q.put(None) # 用None来通知消费者结束
t2.join()
5. 线程同步
在多线程环境中,线程同步是非常重要的。这可以通过锁(Lock)来实现。下面的示例展示了如何使用锁来确保多个线程可以安全地修改共享数据。
import threading# 共享资源
counter = 0
lock = threading.Lock()def update_counter():global counterwith lock:current = countercurrent += 1counter = currentprint(f"Counter: {counter}")threads = []
for i in range(10):t = threading.Thread(target=update_counter)t.start()threads.append(t)for t in threads:t.join()
注意:
t.join()
是一个阻塞调用。当在一个线程中调用join()
方法时,它会阻塞调用它的线程(通常是主线程),直到被join()
的线程(即t
)执行完毕。这意味着主线程将等待t
线程结束其执行,然后才继续执行join()
之后的代码。
join()
方法通常用于确保线程在程序继续执行之前完成其任务,特别是当主线程需要从其他线程中获得结果或确保所有子线程在继续之前完成时。这是一种同步技术,用于协调不同线程间的执行顺序。
这段代码创建了10个线程,每个线程都尝试更新全局变量counter
。通过锁lock
,确保了每次只有一个线程可以修改counter
。
确实,之前的回答中只是简要介绍了threading
模块的一些基础用法。下面将详细介绍threading
模块中几个重要的类和函数,以及它们的参数和用法:
Python Threading模块各类详解
Thread 类
Thread
类是threading
模块中用来创建和管理线程的主要类。主要参数如下:
- target:指定线程启动时要执行的函数。
- args:是一个元组,表示传递给目标函数的参数。
- kwargs:是一个字典,表示传递给目标函数的关键字参数。
- name:为线程指定一个名称,这在调试时很有用。
- daemon:布尔值,指示线程是否是守护线程(守护线程会随着主线程的退出而退出)。
参数
在 Python 的 threading
模块中,daemon
属性是用来指定线程是否为守护线程(daemon thread)。这个属性接受一个布尔值,用来定义线程的行为:
- True:线程被设置为守护线程。守护线程在程序退出时不会阻止程序的终止。也就是说,当主程序结束时,所有的守护线程将被强制终止,无论是否运行完成。
- False:线程被设置为非守护线程(默认值)。非守护线程必须在程序退出前完成或被显式地结束,否则程序会等待这些线程结束后才能完全退出。
守护线程的特性
守护线程通常用于在后台运行的服务或任务,如日志写入、系统监控等。这些任务通常是周期性的,不需要和程序主逻辑同步结束。设置线程为守护线程可以防止这些后台任务延长主程序的生命周期。
示例(守护线程与非守护线程)
下面的代码示例演示了如何设置守护线程,并观察守护线程与非守护线程的行为差异:
- 守护线程(主程序退出守护线程将会被强制退出):
import threading
import timedef daemon_thread():while True:time.sleep(1)print("Daemon thread is running...")# 创建守护线程
daemon = threading.Thread(target=daemon_thread)
daemon.daemon = True# 启动线程
daemon.start()print("Main program exits.")
- 非守护进程(主程序退出,非守护线程照样执行,直到它自己运行完)
import threading
import timedef non_daemon_thread():for i in range(5):time.sleep(1)print("Non-daemon thread is running...")# 创建非守护线程
non_daemon = threading.Thread(target=non_daemon_thread)# 启动线程
non_daemon.start()print("Main program exits.")
示例:
import threadingdef worker(num):"""线程执行的目标函数"""print(f"Worker: {num}")threads = []
for i in range(5):t = threading.Thread(target=worker, args=(i,), name=f"thread-{i}")t.start()threads.append(t)for t in threads:t.join()
Lock 类
Lock
类用于线程同步,主要方法有:
- acquire(blocking=True, timeout=-1):尝试获得锁。blocking
参数为False
时,方法会立即返回一个布尔值,表示是否获取到了锁。timeout
指定等待锁的最长时间。
- release():释放锁。
示例:
import threadinglock = threading.Lock()def worker(num):lock.acquire()try:print(f"Worker: {num}")finally:lock.release()threads = []
for i in range(5):t = threading.Thread(target=worker, args=(i,))t.start()threads.append(t)for t in threads:t.join()
Semaphore 类
信号量是一种更高级的同步机制,允许多个线程同时访问相同的资源,但有限制的数量。
- acquire(blocking=True, timeout=-1):同Lock
的acquire
。
- release():释放信号量。
示例:
import threadingsemaphore = threading.Semaphore(2)def worker(num):with semaphore:print(f"Worker: {num}")threads = []
for i in range(5):t = threading.Thread(target=worker, args=(i,))t.start()threads.append(t)for t in threads:t.join()
Event 类
事件是一个简单的同步机制,主要用于线程间的事件通知。
- set():将事件状态设置为真。
- clear():将事件状态设置为假。
- wait(timeout=None):阻塞线程,直到事件的状态为真或超时。
示例:
import threadingevent = threading.Event()def waiter(event, n):print(f"Thread {n} waiting for event")event.wait()print(f"Thread {n} proceeding")def setter(event):print("Setting event")event.set()t1 = threading.Thread(target=waiter, args=(event, 1))
t2 = threading.Thread(target=waiter, args=(event, 2))
t3 = threading.Thread(target=setter, args=(event,))t1.start()
t2.start()
t3.start()t1.join()
t2.join()
t3.join()
当然,threading
模块除了包括 Thread
, Lock
, Semaphore
, 和 Event
类外,还有其他一些有用的同步原语和工具,可以帮助解决线程之间的协调和状态共享问题。下面介绍几个其他常用的类:
Condition 类
Condition
类用于复杂的线程同步问题。本质上,它是一个围绕锁(Lock)的更高级的锁,允许一个线程等待某个条件,而让其他线程发出条件已满足的信号。
- acquire() / release():与Lock相同,用于获取和释放底层锁。
- wait(timeout=None):调用线程等待,直到被notify()或notify_all()唤醒或超时。
- notify(n=1):唤醒一个或多个正在等待这个条件的线程。n
指定要唤醒的线程数。
- notify_all():唤醒所有等待这个条件的线程。
示例:
import threadingcondition = threading.Condition()def consumer(cond):with cond:print("Consumer waiting")cond.wait()print("Consumer proceed")def producer(cond):with cond:print("Producer ready, notifying consumer")cond.notify_all()t1 = threading.Thread(target=consumer, args=(condition,))
t2 = threading.Thread(target=producer, args=(condition,))t1.start()
t2.start()t1.join()
t2.join()
Barrier 类
Barrier
类用于创建一个同步点,线程必须同时到达这个点才能继续执行。这在某些并行算法中非常有用,需要所有并行任务同时到达某个计算阶段后才能继续。
- parties:这是Barrier类构造函数的主要参数,代表需要到达屏障点的线程数。只有当指定数量的线程都已调用了wait()方法,所有这些线程才会同时被释放继续执行。这个数字必须在创建Barrier时确定,并在其生命周期内保持不变。
- wait():阻塞调用此方法的线程,直到所有线程都已调用此方法。然后所有线程同时释放。
示例:
import threadingbarrier = threading.Barrier(2)def worker(barrier, num):print(f"Worker {num} reaching barrier")barrier.wait()print(f"Worker {num} passed barrier")t1 = threading.Thread(target=worker, args=(barrier, 1))
t2 = threading.Thread(target=worker, args=(barrier, 2))t1.start()
t2.start()t1.join()
t2.join()
Timer 类
Timer
是 Thread
的子类,用于在指定的时间后启动一个操作。
- interval:定时器激活之前的延迟时间(秒)。
- function:定时器激活时要执行的函数。
- args / kwargs:传递给 function 的参数。
示例:
import threadingdef delayed_function():print("Timer activated")timer = threading.Timer(5.0, delayed_function)
timer.start() # 5秒后,打印输出
这些类提供了不同的同步机制,使得多线程程序可以更加精细地控制线程间的交互和协调。在设计多线程应用时,了解和选择合适的同步机制是非常重要的,可以帮助避免死锁和竞争条件等问题。
这些类和方法为Python中的多线程编程提供了丰富的支持,使得可以在多种场景下有效地使用线程。
以上内容是对Python多线程的一个基础介绍,涵盖了创建线程、线程间通信和线程同步的基本概念和方法。在实际应用中,根据具体需求选择合适的同步机制和通信方式是非常重要的。
参考文章
参考文章:python线程创建和传参(33)