来源引用网络知识与某站曹老师视频相互结合学习记录,仅供参考!
Python 基础知识
多线程
多线程的概念
几乎所有的操作系统都支持同时运行多个任务,一个任务通常就是一个程序,每一个运行中的程序就是一个进程。当一个程序运行时,内部可能包含多个顺序执行流,每一个顺序执行流就是一个线程。
为什么使用多线程?
在单线程程序中,任务是一个接一个地顺序执行的;如果某个任务需要等待(例如等待网络响应或文件读取),整个程序会被阻塞,直到该任务完成;而多线程可以让程序在等待某个任务的同时,继续执行其他任务,从而提高程序的整体性能。
前面所有的学习实例都是单线程的,同一时刻,只能执行一个代码,如果遇到阻塞或者异常,则整个项目都停止了;可以这样理解:单线程的程序如同只雇佣一个服务员的餐厅,他必须做完一件事情后才可以做下一个事情;而多线程的程序则如同雇佣多个服务员的餐厅,他们可以同时做多件事情。
进程之间,内存都独立,而同属于一个进程的多个线程共享内存;这个是重要的物理区别。

操作系统-进程、线程
进程是对运行时程序的封装,是系统进行资源调度和分配的的基本单位,实现了操作系统的并发;
线程是进程的子任务,是CPU调度和分派的基本单位,用于保证程序的 实时性,实现进程内部的并发;
一个程序至少有一个进程,一个进程至少有一个线程,线程依赖于进程而存在;
进程在执行过程中拥有独立的内存单元,而多个线程共享进程的内存。
进程间的通信方式
管道(pipe)及命名管道(named pipe):管道可用于具有亲缘关系的父子进程间的通信,有名管道除了具有管道所具有的功能外,它还允许无亲缘关系进程间的通信;
信号(signal):信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生;
消息队列:消息队列是消息的链接表,它克服了上两种通信方式中信号量有限的缺点,具有写权限得进程可以按照一定得规则向消息队列中添加新信息;对消息队列有读权限得进程则可以从消息队列中读取信息;
共享内存:可以说这是最有用的进程间通信方式。它使得多个进程可以访问同一块内存空间,不同进程可以及时看到对方进程中对共享内存中数据得更新;这种方式需要依靠某种同步操作,如互斥锁和信号量等;
信号量:主要作为进程之间及同一种进程的不同线程之间得同步和互斥手段;
套接字:这是一种更为一般得进程间通信机制,它可用于网络中不同机器之间的进程间通信,应用非常广泛。
线程同步的方式
互斥量 Synchronized/Lock:采用互斥对象机制,只有拥有互斥对象的线程才有访问公共资源的权限;因为互斥对象只有一个,所以可以保证公共资源不会被多个线程同时访问;
信号量 Semphare:它允许同一时刻多个线程访问同一资源,但是需要控制同一时刻访问此资源的最大线程数量;
事件(信号),Wait/Notify:通过通知操作的方式来保持多线程同步,还可以方便的实现多线程优先级的比较操作;
通过 threading 模块实现多线程
Python 的 threading 模块是用于实现多线程编程的标准库之一;多线程允许程序在同一时间内执行多个任务,从而提高程序的效率和响应速度。
threading 模块提供了创建和管理线程的工具,使得开发者可以轻松地编写并发程序;
在 Python 中,可以通过继承 threading.Thread 类或直接使用 threading.Thread 构造函数来创建线程;
使用 threading 模块的 Thread 类的构造器来创建线程;
def __init__(self, group=None, target=None, name=None, args=(), kwargs=None, *, daemon=None):
# 构造器参数
group :指定该线程所属的线程组;目前该参数暂未实现,因此它只能是None。
target :指定该线程要调度的目标方法。
name :线程名称,一般不用设置。
args :指定一个元组,以位置参数的形式为target指定的函数参入参数。元组的第一个元素传给target函数的第一个参数,元组的第二个元素传给target函数的第二个参数......依次类推。
kwargs :指定一个字典,以关键字参数的形式为target指定的函数传入参数。
daemon :指定所构建的线程是否为后代线程。一般设置为None即可。

实例:
自定义两个方法;
# 导包
import time
# 定义wishing方法
def wishing():
while True:
# 打印输出
print("Welcome to wishing game!")
# 停顿 1 秒
time.sleep(1)
# 定义cooking方法
def cooking():
while True:
print("Welcome to cooking game!")
time.sleep(1)
# 测试运行
if __name__ == '__main__':
wishing()
cooking()

测试运行后发现一直在执行第一个方法,像这种单线程的要等第一个方法执行完后,顺序执行才能到第二个方法,但是按上述代码中测试模拟的话永远也不会执行到第二个方法,所以此时需要引入多线程;
vb
`# 导包
import time
import threading
# 定义wishing方法
def wishing():
while True:
# 打印输出
print("Welcome to wishing game!")
# 停顿 1 秒
time.sleep(1)
# 定义cooking方法
def cooking():
while True:
print("Welcome to cooking game!")
time.sleep(1)
# 测试运行
if __name__ == '__main__':
# wishing()
# cooking()
# 创建一个线程对象
wishing_thread = threading.Thread(target=wishing)
# 创建第二个线程
cooking_thread = threading.Thread(target=cooking)
# 使用start()方法启动线程
wishing_thread.start()
cooking_thread.start()`
再次运行能够发现两个自定义方法是同时在执行的,有时还能进行并发;

使用参数传参
使用参数时需要注意对应的数据格式;
# 导包
import time
import threading
# 定义wishing方法
def wishing(content):
while True:
# 打印输出
print(content)
# 停顿 1 秒
time.sleep(1)
# 定义cooking方法
def cooking(content):
while True:
print(content)
time.sleep(1)
# 测试运行
if __name__ == '__main__':
# wishing()
# cooking()
# 创建一个线程对象,使用args参数,需注意是元组格式,传参时要在后面加上一个逗号,这样就是元组,否则会被识别为字符串
wishing_thread = threading.Thread(target=wishing, args=("Welcome to wishing game!",))
# 创建第二个线程,使用kwargs参数,字典格式
cooking_thread = threading.Thread(target=cooking, kwargs={"content": "Welcome to cooking game!"})
# 使用start()方法启动线程
wishing_thread.start()
cooking_thread.start()
常用类、方法及属性
1. 核心类
|--------------------------------|-------------------|--------------------------------------|
| 类/方法/属性 | 说明 | 示例 |
| threading.Thread | 线程类,用于创建和管理线程。 | t = Thread(target=func, args=(1,)) |
| threading.Lock | 互斥锁(原始锁)。 | lock = Lock() |
| threading.RLock | 可重入锁(同一线程可多次获取)。 | rlock = RLock() |
| threading.Event | 事件对象,用于线程同步。 | event = Event() |
| threading.Condition | 条件变量,用于复杂线程协调。 | cond = Condition() |
| threading.Semaphore | 信号量,控制并发线程数。 | sem = Semaphore(3) |
| threading.BoundedSemaphore | 有界信号量(防止计数超过初始值)。 | b_sem = BoundedSemaphore(2) |
| threading.Timer | 定时器线程,延迟执行。 | timer = Timer(5.0, func) |
| threading.local | 线程局部数据(各线程独立存储)。 | local_data = threading.local() |
2. Thread 对象常用方法/属性
|------------------------|----------------------|-----------------------|
| 方法/属性 | 说明 | 示例 |
| start() | 启动线程。 | t.start() |
| run() | 线程执行的方法(可重写)。 | 自定义类时覆盖此方法 |
| join(timeout=None) | 阻塞当前线程,直到目标线程结束。 | t.join() |
| is_alive() | 检查线程是否在运行。 | if t.is_alive(): |
| name | 线程名称(可修改)。 | t.name = "Worker-1" |
| daemon | 守护线程标志(主线程退出时自动结束)。 | t.daemon = True |
| ident | 线程标识符(未启动时为 None)。 | print(t.ident) |
3. Lock/RLock 常用方法
|----------------------------------------|--------------|-------------------------|
| 方法 | 说明 | 示例 |
| acquire(blocking=True, timeout=-1) | 获取锁(阻塞或非阻塞)。 | lock.acquire() |
| release() | 释放锁。 | lock.release() |
| locked() | 检查锁是否被占用。 | if not lock.locked(): |
4. Event 常用方法
|------------------------|------------------|----------------------|
| 方法 | 说明 | 示例 |
| set() | 设置事件为真,唤醒所有等待线程。 | event.set() |
| clear() | 重置事件为假。 | event.clear() |
| wait(timeout=None) | 阻塞直到事件为真或超时。 | event.wait(2.0) |
| is_set() | 检查事件状态。 | if event.is_set(): |
5. Condition 常用方法
|------------------------|------------------|---------------------|
| 方法 | 说明 | 示例 |
| wait(timeout=None) | 释放锁并阻塞,直到被通知或超时。 | cond.wait() |
| notify(n=1) | 唤醒最多 n 个等待线程。 | cond.notify(2) |
| notify_all() | 唤醒所有等待线程。 | cond.notify_all() |
6. 模块级函数/属性
|--------------------------------|--------------|-------------------------------------------------------------|
| 函数/属性 | 说明 | 示例 |
| threading.active_count() | 返回当前活跃线程数。 | print(threading.active_count()) |
| threading.current_thread() | 返回当前线程对象。 | print(threading.current_thread().name) |
| threading.enumerate() | 返回所有活跃线程的列表。 | for t in threading.enumerate(): |
| threading.main_thread() | 返回主线程对象。 | if threading.current_thread() is threading.main_thread(): |
| threading.get_ident() | 返回当前线程的标识符。 | print(threading.get_ident()) |
未完待续......