快速学习Python基础知识详细图文教程18--多线程

来源引用网络知识与某站曹老师视频相互结合学习记录,仅供参考!

Python 基础知识

多线程

多线程的概念

Python3 多线程

几乎所有的操作系统都支持同时运行多个任务,一个任务通常就是一个程序,每一个运行中的程序就是一个进程。当一个程序运行时,内部可能包含多个顺序执行流,每一个顺序执行流就是一个线程。

为什么使用多线程?

在单线程程序中,任务是一个接一个地顺序执行的;如果某个任务需要等待(例如等待网络响应或文件读取),整个程序会被阻塞,直到该任务完成;而多线程可以让程序在等待某个任务的同时,继续执行其他任务,从而提高程序的整体性能。

前面所有的学习实例都是单线程的,同一时刻,只能执行一个代码,如果遇到阻塞或者异常,则整个项目都停止了;可以这样理解:单线程的程序如同只雇佣一个服务员的餐厅,他必须做完一件事情后才可以做下一个事情;而多线程的程序则如同雇佣多个服务员的餐厅,他们可以同时做多件事情。

进程之间,内存都独立,而同属于一个进程的多个线程共享内存;这个是重要的物理区别。

操作系统-进程、线程

进程是对运行时程序的封装,是系统进行资源调度和分配的的基本单位,实现了操作系统的并发;

线程是进程的子任务,是CPU调度和分派的基本单位,用于保证程序的 实时性,实现进程内部的并发;

一个程序至少有一个进程,一个进程至少有一个线程,线程依赖于进程而存在;

进程在执行过程中拥有独立的内存单元,而多个线程共享进程的内存。

进程间的通信方式

管道(pipe)及命名管道(named pipe):管道可用于具有亲缘关系的父子进程间的通信,有名管道除了具有管道所具有的功能外,它还允许无亲缘关系进程间的通信;

信号(signal):信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生;

消息队列:消息队列是消息的链接表,它克服了上两种通信方式中信号量有限的缺点,具有写权限得进程可以按照一定得规则向消息队列中添加新信息;对消息队列有读权限得进程则可以从消息队列中读取信息;

共享内存:可以说这是最有用的进程间通信方式。它使得多个进程可以访问同一块内存空间,不同进程可以及时看到对方进程中对共享内存中数据得更新;这种方式需要依靠某种同步操作,如互斥锁和信号量等;

信号量:主要作为进程之间及同一种进程的不同线程之间得同步和互斥手段;

套接字:这是一种更为一般得进程间通信机制,它可用于网络中不同机器之间的进程间通信,应用非常广泛。

线程同步的方式

互斥量 Synchronized/Lock:采用互斥对象机制,只有拥有互斥对象的线程才有访问公共资源的权限;因为互斥对象只有一个,所以可以保证公共资源不会被多个线程同时访问;

信号量 Semphare:它允许同一时刻多个线程访问同一资源,但是需要控制同一时刻访问此资源的最大线程数量;

事件(信号),Wait/Notify:通过通知操作的方式来保持多线程同步,还可以方便的实现多线程优先级的比较操作;

通过 threading 模块实现多线程

Python 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()) |

未完待续......

相关推荐
想要入门的程序猿1 小时前
Qt插件QPluginLoader
开发语言·qt
Rabitebla1 小时前
C++11 新特性详解(一):列表初始化、initializer_list 与右值引用
java·开发语言·数据结构·c++·算法·leetcode·list
洋不写bug1 小时前
JavaComparable、Comparator、Cloneable接口解析,深拷贝浅拷贝详细解析
android·java·开发语言
吴声子夜歌2 小时前
正则指引——Golang
开发语言·golang·正则表达式
AI Dog2 小时前
MathHub:数学建模学习社区,内置数学建模智能体Modulus
学习·数学建模·智能体·modulus·数学建模智能体
坐吃山猪2 小时前
WebFlux_学习Subscriber总结
java·开发语言·学习·webflux
GlueNa2SiO32 小时前
10-Docker生产环境部署与K8s入门
笔记·学习·docker·容器·kubernetes
白露与泡影2 小时前
Java面试题及答案整理(2026年金九银十最新版,持续更新)
java·开发语言
茯苓gao2 小时前
无感FOC核心原理:没有编码器,电机如何获得转子电角度?
笔记·嵌入式硬件·学习