python多线程
一. 线程:
线程是进程中执行代码的一个分支,每个执行分支(线程)要想工作执行代码需要cpu进行调度 ,也就是说线程是cpu调度的基本单位,每个进程至少都有一个线程,而这个线程就是我们通常说的主线程。
二. 线程的使用:
1. 导入线程模块
#导入线程模块
import threading
2. 线程类Thread参数说明
Thread([group [, target [, name [, args [, kwargs]]]]])
- group: 线程组,目前只能使用None
- target: 执行的目标任务名
- args: 以元组的方式给执行任务传参
- kwargs: 以字典方式给执行任务传参
- name: 线程名,一般不用设置
3. 启动线程
启动线程使用start方法
import threading import time def sing(): # 获取当前线程 for i in range(3): print("唱歌。。。") time.sleep(0.2) def dance(): # 获取当前线程 for i in range(3): print("跳舞。。。") time.sleep(0.2) if __name__ == '__main__': singthread = threading.Thread(target=sing) dancethread = threading.Thread(target=dance) singthread.start() dancethread.start()
执行结果:
唱歌。。。
跳舞。。。
跳舞。。。
唱歌。。。
唱歌。。。
跳舞。。。
Process finished with exit code 0
4. 创建子线程时参数传递 # 跟子进程类似
4.1 args元组方式传参
import threading import time # 带有参数的任务 def task(count): for i in range(count): print("任务执行中..", i) time.sleep(0.2) else: print("任务执行完成") if __name__ == '__main__': # 创建子线程 # args: 以元组的方式给任务传入参数 sub_thread = threading.Thread(target=task, args=(5,)) sub_thread.start()
执行结果:
任务执行中.. 0 任务执行中.. 1 任务执行中.. 2 任务执行中.. 3 任务执行中.. 4 任务执行完成 Process finished with exit code 0
4.2 kwargs字典方式传参
import threading import time # 带有参数的任务 def task(count): for i in range(count): print("任务执行中..", i) time.sleep(0.2) else: print("任务执行完成") if __name__ == '__main__': # 创建子线程 # kwargs: 表示以字典方式传入参数 sub_thread = threading.Thread(target=task, kwargs={"count": 3}) sub_thread.start()
执行结果:
任务执行中.. 0 任务执行中.. 1 任务执行中.. 2 任务执行完成 Process finished with exit code 0
5. 多个线程的执行时无序的
- 线程之间执行是无序的,它是由cpu调度决定的 ,cpu调度哪个线程,哪个线程就先执行,没有调度的线程不能执行。
- 进程之间执行也是无序的,它是由操作系统调度决定的,操作系统调度哪个进程,哪个进程就先执行,没有调度的进程不能执行。
6. 主线程会等待所有的子线程执行结束再结束
7. 设置守护线程
将子线程设置为守护主线程后,主线程结束时会销毁子线程
import threading import time # 测试主线程是否会等待子线程执行完成以后程序再退出 def show_info(): for i in range(5): print("test:", i) time.sleep(0.5) if __name__ == '__main__': # 创建子线程守护主线程 # daemon=True 守护主线程 # 守护主线程方式1 sub_thread = threading.Thread(target=show_info, daemon=True) # 设置成为守护主线程,主线程退出后子线程直接销毁不再执行子线程的代码 # 守护主线程方式2 # sub_thread.setDaemon(True) sub_thread.start() # 主线程延时1秒 time.sleep(1) print("over")
执行结果
test: 0 test: 1 over Process finished with exit code 0
8. 线程等待
线程对象..join() # 等待线程执行完,再执行后续代码
8. 线程之间可以共享全局变量
import threading import time # 定义全局变量 my_list = list() # 写入数据任务 def write_data(): for i in range(5): my_list.append(i) time.sleep(0.1) print("write_data:", my_list) # 读取数据任务 def read_data(): print("read_data:", my_list) if __name__ == '__main__': # 创建写入数据的线程 write_thread = threading.Thread(target=write_data) # 创建读取数据的线程 read_thread = threading.Thread(target=read_data) write_thread.start() # 主线程等待写入线程执行完成以后代码在继续往下执行 write_thread.join() print("开始读取数据啦") read_thread.start()
执行结果:
write_data: [0, 1, 2, 3, 4] 开始读取数据啦 read_data: [0, 1, 2, 3, 4] Process finished with exit code 0
9. 多个线程共享变量可能出现错误:
import threading # 定义全局变量 g_num = 0 # 循环一次给全局变量加1 def sum_num1(): for i in range(1000000): global g_num g_num += 1 print("sum1:", g_num) # 循环一次给全局变量加1 def sum_num2(): for i in range(1000000): global g_num g_num += 1 print("sum2:", g_num) if __name__ == '__main__': # 创建两个线程 first_thread = threading.Thread(target=sum_num1) second_thread = threading.Thread(target=sum_num2) # 启动线程 first_thread.start() # 启动线程 second_thread.start()
执行结果:
sum1: 1181587 sum2: 1353542 Process finished with exit code 0
10. 线程锁:
互斥锁: 对共享数据进行锁定,保证同一时刻只能有一个线程去操作。
- 互斥锁是多个线程一起去抢,抢到锁的线程先执行,没有抢到锁的线程需要等待,等互斥锁使用完释放后,其它等待的线程再去抢这个锁。
10.1 创建锁对象
# 创建锁 mutex = threading.Lock()
10.2 上锁
# 上锁 mutex.acquire()
10.3 释放锁
# 释放锁 mutex.release()
10.4 解决9中的问题:
import threading # 定义全局变量 g_num = 0 # 创建锁 mutex = threading.Lock() # 循环一次给全局变量加1 def sum_num1(): # 上锁 mutex.acquire() for i in range(1000000): global g_num g_num += 1 # 释放锁 mutex.release() print("sum1:", g_num) # 循环一次给全局变量加1 def sum_num2(): mutex.acquire() for i in range(1000000): global g_num g_num += 1 mutex.release() print("sum2:", g_num) if __name__ == '__main__': # 创建两个线程 first_thread = threading.Thread(target=sum_num1) second_thread = threading.Thread(target=sum_num2) # 启动线程 first_thread.start() # 启动线程 second_thread.start()
执行结果:
sum1: 1000000 sum2: 2000000 Process finished with exit code 0
11. 死锁
死锁: 一直等待对方释放锁的情景就是死锁
import threading # 创建锁 mutex = threading.Lock() # 循环一次给全局变量加1 def task(index): mutex.acquire() my_list = [1,2,3] if index >= len(my_list): print("指针越界") # mutex.release() return print(my_list[i]) mutex.release() if __name__ == '__main__': for i in range(10): # 创建线程 task_thread = threading.Thread(target=task, args=(i,)) # 启动线程 task_thread.start()
执行结果:
1
2
3
指针越界
说明:程序出现死锁
12. 线程和进程
- 线程是依附在进程里面的,没有进程就没有线程。
- 一个进程默认提供一条线程,进程可以创建多个线程。
区别对比
-
进程之间不共享全局变量
-
线程之间共享全局变量,但是要注意资源竞争的问题,解决办法: 互斥锁或者线程同步
-
创建进程的资源开销要比创建线程的资源开销要大
-
进程是操作系统资源分配的基本单位,线程是CPU调度的基本单位
-
线程不能够独立执行,必须依存在进程中
-
多进程开发比单进程多线程开发稳定性要强
优缺点对比
- 进程优缺点:
- 优点:可以用多核
- 缺点:资源开销大
- 线程优缺点:
- 优点:资源开销小
- 缺点:不能使用多核
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构