这篇文章主要讲解了“Python的多线程多进程是什么”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Python的多线程多进程是什么”吧!
前提
-
我是参考 Github Python 100 天的文章写的,再结合自己的小练习,总结
-
最近在面大厂,发现许多大厂都会问 Python 的多线程、多进程,所以我觉得很有必要总结学习下
什么是进程
操作系统中执行的一个程序,类似微信、QQ,每个程序都是一个进程
概念
-
它是 CPU 最小资源分配单元
-
操作系统会给进程分配内存空间,每个进程都会有自己的地址空间、数据栈以及其他用于跟踪进程执行的辅助数据
-
操作系统管理所有进程的执行,为它们合理的分配资源
fork、spawn
-
进程可以通过 fork、spawn 的方式来创建新的进程来执行其他任务
-
不过新的进程有自己独立的内存空间、数据栈
-
因此不同进程间需要通过通信机制(IPC)来实现数据共享
常见通信机制
-
管道
-
信号
-
套接字
-
共享内存区
什么是线程
-
进程中可以拥有多个并发的执行线索
-
它是 CPU 最小调度的执行单元
特点
-
同一个进程下的线程共享相同的上下文
-
相对于进程来说,线程间的信息共享和通信更加容易
单核 CPU 系统注意事项
-
真正的并发是不可能的
-
因为在某个时刻,CPU 只能运行唯一的一个线程
-
多个线程共享了 CPU 的执行时间
多线程的好处
-
提升程序的性能和改善用户体验
-
今天日常使用的软件几乎都用到了多线程
多线程的坏处
-
站在其他进程的角度,多线程的程序对其他程序并不友好,因为它占用了更多的 CPU 执行时间,导致其他程序无法获得足够的 CPU 执行时间
-
编写和调试多线程的程序对开发者要求较高
Python 实现并发编程的方式
-
多进程
-
多线程
-
多进程+多线程
Python 中的多进程
Linux 下的 fork 函数
-
Linux 操作系统上提供了 fork() 系统调用来创建进程
-
调用 fork() 函数的是父进程
-
创建的是子进程
-
子进程是父进程的拷贝
-
但子进程有自己的 PID
-
fork() 函数非常特殊,它会返回两次,父进程中调用 fork() 会返回子进程的 PID,子进程中调用 fork() 得到的都是0
Python 提供的 fork 函数
os 模块提供了 fork()
Window 下没有fork()的调用
-
实现跨平台的多进程变成,可以使用 multiprocessing 模块的 Process 类来创建子进程
-
还提供了更高级的封装,例如批量启动进程的进程池 pool、用于进程间同喜你的队列 Queue 和管道 Pipe
使用多进程和不使用多进程的区别(写代码)
不使用多进程
from random import randintfrom time import time, sleepdef download_task(filename):print('开始下载%s...' % filename) time_to_download = randint(5, 10) sleep(time_to_download)print('%s下载完成! 耗费了%d秒' % (filename, time_to_download))def main(): start = time() download_task('Python从入门到住院.pdf') download_task('Peking Hot.avi') end = time()print('总共耗费了%.2f秒.' % (end - start))if __name__ == '__main__': main()
执行结果
开始下载Python从入门到住院.pdf... Python从入门到住院.pdf下载完成! 耗费了10秒 开始下载Peking Hot.avi... Peking Hot.avi下载完成! 耗费了9秒 总共耗费了19.02秒.
可以看到需要先等第一个文件下载完才能下载第二个文件,效率很低
使用多进程
from random import randintfrom time import time, sleepfrom multiprocessing import Processdef download_task(filename):print('开始下载%s...' % filename) time_to_download = randint(5, 10) sleep(time_to_download)print('%s下载完成! 耗费了%d秒' % (filename, time_to_download))def main2(): start = time() p1 = Process(target=download_task,args=("Python从入门到住院.pdf",)) p1.start() p2 = Process(target=download_task, args=("Peking Hot.avi",)) p2.start() p1.join() p2.join() end = time()print('总共耗费了%.2f秒.' % (end - start))if __name__ == '__main__': main2()
执行结果
开始下载Python从入门到住院.pdf... 开始下载Peking Hot.avi... Python从入门到住院.pdf下载完成! 耗费了6秒 Peking Hot.avi下载完成! 耗费了10秒 总共耗费了10.17秒.
两个任务同时执行,总耗时不再是两个任务的时间总和
知识点
-
Process:通过 Process 类创建进程对象
-
target:通过 target 参数传入一个函数名来表示进程启动后要执行的代码
-
args:是一个元组,代表传递给函数的参数列表
-
start:Process 的 start() 方法来启动进程
-
join:Process 的 join() 方法表示等待进程执行结束,才会往下执行
Python 中的多线程
前言
推荐 threading 模块来实现多线程编程,它提供了更好的面向对象封装
多线程的实现方式
#!/usr/bin/env python# -*- coding: utf-8 -*-"""__title__ = __Time__ = 2021/3/19 18:17 __Author__ = 小菠萝测试笔记 __Blog__ = https://www.cnblogs.com/poloyy/"""from random import randintfrom threading import Threadfrom time import time, sleepdef download_task(filename):print('开始下载%s...' % filename) time_to_download = randint(5, 10) sleep(time_to_download)print('%s下载完成! 耗费了%d秒' % (filename, time_to_download))def main3(): start = time() p1 = Thread(target=download_task,args=("Python从入门到住院.pdf",)) p1.start() p2 = Process(target=download_task, args=("Peking Hot.avi",)) p2.start() p1.join() p2.join() end = time()print('总共耗费了%.2f秒.' % (end - start))if __name__ == '__main__': main3()
执行结果
开始下载Python从入门到住院.pdf... 开始下载Peking Hot.avi... Peking Hot.avi下载完成! 耗费了6秒 Python从入门到住院.pdf下载完成! 耗费了8秒 总共耗费了8.01秒.
一样执行效率高很多
自定义线程类
#!/usr/bin/env python# -*- coding: utf-8 -*-"""__title__ = __Time__ = 2021/3/19 18:17 __Author__ = 小菠萝测试笔记 __Blog__ = https://www.cnblogs.com/poloyy/"""from random import randintfrom threading import Threadfrom time import time, sleepclass downLoadTask(Thread):def __init__(self,filename): super().__init__() self.filename = filenamedef run(self) -> None:print('开始下载%s...' % self.filename) time_to_download = randint(5, 10) sleep(time_to_download)print('%s下载完成! 耗费了%d秒' % (self.filename, time_to_download))def main3(): start = time() p1 = downLoadTask("Python从入门到住院.pdf") p2 = downLoadTask("Peking Hot.avi") p1.start() p2.start() p1.join() p2.join() end = time()print('总共耗费了%.2f秒.' % (end - start))if __name__ == '__main__': main3()
执行结果
开始下载Python从入门到住院.pdf... 开始下载Peking Hot.avi... Peking Hot.avi下载完成! 耗费了6秒 Python从入门到住院.pdf下载完成! 耗费了9秒 总共耗费了9.00秒.
也是一样的高效运行
重点知识:start 和 run 方法的区别
比较点 | start | run |
作用 | 启动线程,获取 CPU 时间片 | 运行线程指定的代码块 |
线程状态 | 可运行状态 | 运行状态 |
调用次数 | 一个线程只能调用一次 | 可以重复调用 |
运行线程 | 创建了一个子线程,线程名是自己命名的 | 在主线程中调用了一个普通函数 |
注意点 | 想用多线程,必须调用 start() |
Python 中的协程
什么是协程
Python 中,单线程+异步 I/O 的编程模型
协程的优势
-
极高的执行效率
-
子程序切换不是线程切换,而是由程序本身控制,没有线程切换的开销
-
不需要多线程的所机制,只有一个线程,所以不存在同时写变量冲突,在协程中控制共享资源不用加锁,只需要判断状态就好了,所以执行效率比多线程高很多
重点
要充分利用 CPU 的多核特性,应该使用多进程+协程的方式
感谢各位的阅读,以上就是“Python的多线程多进程是什么”的内容了,经过本文的学习后,相信大家对Python的多线程多进程是什么这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是云,小编将为大家推送更多相关知识点的文章,欢迎关注!