新手如何理解Python并发编程,有哪些基础知识
Admin 2022-09-16 群英技术资讯 283 次浏览
并发编程是实现多任务协同处理,改善系统性能的方式。Python中实现并发编程主要依靠
进程(Process):进程是计算机中的程序关于某数据集合的一次运行实例,是操作系统进行资源分配的最小单位
线程(Thread):线程被包含在进程之中,是操作系统进行程序调度执行的最小单位
协程(Coroutine):协程是用户态执行的轻量级编程模型,由单一线程内部发出控制信号进行调度
直接上一张图看看三者概念间的关系。
这张图说明了什么?首先,一条线程是进程中一个单一的顺序控制流,一个进程可以并发多个线程执行不同任务。协程由单一线程内部发出控制信号进行调度,而非受到操作系统管理,因此协程没有切换开销和同步锁机制,具有极高的执行效率。
进程、线程、协程间的特性决定了它们的应用场景不同:
协程常用于IO密集型工作,例如网络资源请求等;而进程、线程常用于计算密集型工作,例如科学计算、人工神经网络等。
接下来对每种并发编程方法进行详细阐述。
Python多进程依赖于标准库mutiprocessing,进程类Process的常用方法如下
序号 | 方法 | 含义 |
---|---|---|
1 | start() | 创建一个Process子进程实例并执行该实例的run()方法 |
2 | run() | 子进程需要执行的目标任务 |
3 | join() | 主进程阻塞等待子进程直到子进程结束才继续执行,可以设置等待超时时间timeout |
4 | terminate() | 终止子进程 |
5 | is_alive() | 判断子进程是否终止 |
6 | daemon | 设置子进程是否随主进程退出而退出 |
创建多进程任务的实例如下
import os, time import multiprocessing class myProcess(multiprocessing.Process): def __init__(self, *args, **kwargs) -> None: super().__init__() self.name = kwargs['name'] def run(self): print("process name:", self.name) for i in range(10): print(multiprocessing.current_process(), "process pid:", os.getpid(), "正在执行...") time.sleep(0.2) if __name__ == "__main__": task = myProcess(name="testProcess") task.start() task.join() # 该语句会阻塞主进程直至子进程结束 print("----------------")
注意:Windows系统在子进程结束后会立即自动清除子进程实例;而Linux系统子进程实例仅当主进程结束后才被回收,在子进程结束但主进程仍在运行的时间内处于僵尸进程状态,会造成性能损失甚至死锁。对子进程实例的手动回收可以通过
p.terminate() p.join()
完成,此外,start()函数也有清除僵尸进程的功能。在使用多进程处理任务时并非进程越多越好,因为进程切换会造成性能开销。
Python多线程依赖于标准库threading,线程类Thread的常用方法如下表:
序号 | 方法 | 含义 |
---|---|---|
1 | start() | 创建一个Thread子线程实例并执行该实例的run()方法 |
2 | run() | 子线程需要执行的目标任务 |
3 | join() | 主进程阻塞等待子线程直到子线程结束才继续执行,可以设置等待超时时间timeout |
4 | is_alive() | 判断子线程是否终止 |
5 | daemon | 设置子线程是否随主进程退出而退出 |
关于线程与进程的关系,还有一个很生动的例子
把一条公路看作一道进程,那么公路上的各个车道就是进程中的各个线程。这些线程(车道)共享了进程(道路)的公共资源;这些线程(车道)之间可以并发执行(各个车道相对独立),也可以互相同步(交通信号灯)。
rsrc = 10 lock = threading.Lock() def task1(name): global rsrc, lock for i in range(5): with lock: rsrc += 1 print("task1:", rsrc) return name + "has been done!" def task2(name): global rsrc, lock for i in range(5): lock.acquire() rsrc -= 1 print("task2:", rsrc) lock.release() return name + "has been done!"
结果如下
在多线程并发过程中,若没有控制好线程间的执行逻辑,将可能产生死锁现象,可以使用with关键词在线程访问临界区结束后自动释放锁,也可使用release()方法手动释放句柄。
协程适用于I/O密集型而非计算密集型场景。在协程发起I/O请求后返回结果前往往有大量闲置时间——该时间可能用于网络数据传输、获取协议头、服务器查询数据库等,而I/O请求本身并不耗时,因此协程可以发送一个请求后让渡给系统干别的事,这就是协程提高性能的原因。
协程编程的框架如下:
一个嵌套协程的示例如下:
import asyncio, time # 内层协程 async def do_some_work(x): print('Waiting: ', x) await asyncio.sleep(x) return 'Done after {}s'.format(x) def OnCallBack(res): print(res.result()) # 外层协程main async def main(): # 创建三个协程对象并封装成任务 task1 = asyncio.ensure_future(do_some_work(1)) task2 = asyncio.ensure_future(do_some_work(8)) task3 = asyncio.ensure_future(do_some_work(4)) # 添加回调 task1.add_done_callback(OnCallBack) task2.add_done_callback(OnCallBack) task3.add_done_callback(OnCallBack) # 内层任务列表 tasks = [task1, task2, task3] # 将列表转为可等待对象 dones, pendings = await asyncio.wait(tasks) # 外层协程func async def func(): for i in range(5): print("func:", i) # 外层任务列表 tasks = [asyncio.ensure_future(func()), asyncio.ensure_future(main())] # 创建事件循环 loop = asyncio.get_event_loop() start = time.time() # 监听异步任务 loop.run_until_complete(asyncio.wait(tasks)) end = time.time() print("总耗时:", end - start)
看了这么多概念可能有点晕了,下面这张表总结了本文的内容。总得来说,进程、线程、协程各有各的应用场景,不能说多进程、多线程、多协程就一定好,而是要根据具体的使用情况来确定。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:mmqy2019@163.com进行举报,并提供相关证据,查实之后,将立刻删除涉嫌侵权内容。
猜你喜欢
这篇文章主要介绍了通过Python实现一个简单的电影售票系统,文中的示例代码对我们学习Python有一定的帮助,感兴趣的小伙伴可以跟随小编一起学习一下
这篇文章主要给大家分析python实现提取html文本的方法,小编觉得比较实用,对新手学习python有一定的帮助,因此分享给大家做个参考,感兴趣的朋友可以了解看看,接下来我们一起学习一下吧。
偶然从pytorch讨论论坛中看到的一个问题,KL divergence different results from tf,kl divergence 在TensorFlow中和pytorch中计算结果不同,平时没有注意到,记录下
这篇文章主要为大家详细介绍了Python实现信息管理系统,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
这篇文章主要介绍了Python如何一行输入多个数,并存入列表,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
成为群英会员,开启智能安全云计算之旅
立即注册Copyright © QY Network Company Ltd. All Rights Reserved. 2003-2020 群英 版权所有
增值电信经营许可证 : B1.B2-20140078 粤ICP备09006778号 域名注册商资质 粤 D3.1-20240008