首页 > 系统相关 >python协程和子进程混用编程尝试

python协程和子进程混用编程尝试

时间:2022-12-16 11:57:10浏览次数:42  
标签:task 协程 python pid 混用 Resuming 131008 print 119276

使用python编程,当程序是IO密集型,很多网友都推荐使用协程代替线程,因为python的多线程因为GIL的原因,并不能使用计算机CPU多核;而协程是微线程,性能更好,资源消耗更少,适合于多并发。

如果程序是计算密集型,则推荐使用多进程编程,因为多进程可以利用到计算机的多核CPU并行计算。

当程序复杂时,必不可少的可能会使用协程和多进程混合编程。

这里做了一个协程和多进程混合编程的尝试,测试代码如下:

 1 import asyncio
 2 import time
 3 import threading
 4 from multiprocessing import Process
 5 import os
 6 
 7 async def task_a(pid):
 8     print("===begin task a.===")
 9     for i in range(5):
10         print("in task_a = %s" % pid)
11         await asyncio.sleep(1)
12         print("Resuming task_a = %s" % pid)
13     print("===finish task a.===")
14     return
15 
16 
17 async def task_b(pid):
18     print("===begin task b.===")
19     p = Process(target=new_loop)
20     p.start()
21     p.join()
22     print("===finish task b.===")
23 
24 
25 async def task_c(pid):
26     print("===begin task c.===")
27     for i in range(5):
28         print("in task_c = %s" % pid)
29         await asyncio.sleep(2)
30         print("Resuming task_c = %s" % pid)
31     print("===finish task c.===")
32     return
33 
34 
35 def new_loop():
36     new_p_loop = asyncio.get_event_loop()
37     print("in new process, pid = {}, ppid={}".format(os.getpid(), os.getppid()))
38 
39     tasks = [task_c(os.getpid())]
40     new_p_loop.run_until_complete(asyncio.wait(tasks))
41     print("finished pid=%s" % (os.getpid()))
42 
43 
44 if __name__ == '__main__':
45     print("main pid={}, ppid={}".format(os.getpid(), os.getppid()))
46     start = time.perf_counter()
47 
48     loop = asyncio.get_event_loop()
49     tasks = [task_a(os.getpid()), task_b(os.getpid())]
50     loop.run_until_complete(asyncio.wait(tasks))
51 
52     end = time.perf_counter()
53     print("cost time = %s" % (end - start))

代码在主进程中使用了协程,创建了两个task:task_a, task_b,这个用于模仿主进程是并发接受消息,然后执行task任务。

task_b中启动了子进程,子进程中又同时启动了协程loop,执行task_c。这个用于模仿task_b是个计算密集型任务,适合启动子进程进行任务执行,从而不影响主进程的运行。

测试代码运行后,打印结果如下:

 1 main pid=125948, ppid=128144
 2 ===begin task a.===
 3 in task_a = 125948
 4 ===begin task b.===
 5 in new process, pid = 121040, ppid=125948
 6 ===begin task c.===
 7 in task_c = 121040
 8 Resuming task_c = 121040
 9 in task_c = 121040
10 Resuming task_c = 121040
11 in task_c = 121040
12 Resuming task_c = 121040
13 in task_c = 121040
14 Resuming task_c = 121040
15 in task_c = 121040
16 Resuming task_c = 121040
17 ===finish task c.===
18 finished pid=121040
19 ===finish task b.===
20 Resuming task_a = 125948
21 in task_a = 125948
22 Resuming task_a = 125948
23 in task_a = 125948
24 Resuming task_a = 125948
25 in task_a = 125948
26 Resuming task_a = 125948
27 in task_a = 125948
28 Resuming task_a = 125948
29 ===finish task a.===
30 cost time = 14.116450799999999

从测试结果来看,程序运行并没有按照我们的预期执行。task_a单独执行需要耗时5秒,task_c单独执行需要耗时10秒,程序执行完耗时14秒多,感觉像串行执行,并没有达到并行执行效果。

从打印日志也可以看出,程序先执行task_a,等待task_a sleep后,切换到执行task_b,task_b启动子进程,然后是先执行完task_c,然后才继续执行task_a,两者没有并行执行。

经过尝试,发现问题出现task_b的p.join()语句上。p.join()的目的是等待子进程执行结束,再退出。然而task_b在执行p.join()语句阻塞时,不会切换到task_a执行,而是将整个主进程的主线程阻塞了。

现将task_b函数代码修改如下:

 1 async def task_b(pid):
 2     print("===begin task b.===")
 3     p = Process(target=new_loop)
 4     p.start()
 5     while True:
 6         if not p.is_alive():
 7             break
 8         else:
 9             await asyncio.sleep(1)
10     print("===finish task b.===")

程序运行结果如下:

main pid=119276, ppid=128048
===begin task a.===
in task_a = 119276
===begin task b.===
in new process, pid = 131008, ppid=119276
===begin task c.===
in task_c = 131008
Resuming task_a = 119276
in task_a = 119276
Resuming task_a = 119276
in task_a = 119276
Resuming task_c = 131008
in task_c = 131008
Resuming task_a = 119276
in task_a = 119276
Resuming task_a = 119276
in task_a = 119276
Resuming task_c = 131008
in task_c = 131008
Resuming task_a = 119276
===finish task a.===
Resuming task_c = 131008
in task_c = 131008
Resuming task_c = 131008
in task_c = 131008
Resuming task_c = 131008
===finish task c.===
finished pid=131008
===finish task b.===
cost time = 11.0163683

从打印日志来看,代码修改后,task_a和task_c基本是并行执行,耗时为11秒。

总结:

协程task之间并发执行,其实是在同一个线程中切换执行,task切换条件是task有I/O操作或者是asyncio.sleep()操作。

像p.join()操作不会触发task之间的切换操作,会导致协程task阻塞。

 

标签:task,协程,python,pid,混用,Resuming,131008,print,119276
From: https://www.cnblogs.com/muliublog/p/16986633.html

相关文章

  • golang 协程并发代码 demo
    有时我们可能想既在外层循环中实现多协程并发,还想在内层循环中实现多协程并发,那么我们需要同时在内层和外层使用​​WaitGroup()​​来控制主协程不退出。下面是一个demo......
  • python编程中的if __name__ == 'main': 的作用和原理
    大多数编排得好一点的脚本或者程序里面都有这段if__name__=='main':,虽然一直知道他的作用,但是一直比较模糊,收集资料详细理解之后与打架分享。  1、这段代码的功能 ......
  • Python的Django框架中forms表单类的使用方法详解
    FormForm的验证思路前端:form表单后台:创建form类,当请求到来时,先匹配,匹配出正确和错误信息。Django的Form验证实例:创建project,进行基础配置文件配置settings.pysettings.py之c......
  • 将Python程序打包成Linux可执行文件
    将Python程序打包成Linux可执行文件安装环境首先我们要安装pip,命令如下:sudoaptinstallpython3-pip使用的工具是pyinstaller,打开终端输入sudopipinstallpyin......
  • python 操作redis有序集合
      https://feeler.blog.csdn.net/article/details/103100452?spm=1001.2101.3001.6650.1&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogComme......
  • Python requests.post 发送中文 'latin-1' codec can't encode characters in positio
    headers={"Content-type":"application/json;charset=utf-8","Authorization":"bearer"+token}data={#上游任务id名称'upstreamTaskI......
  • Windows下安装mysql-python(MySQLdb)问题记录
    今天太倒霉了,这篇文章马上就要写完了,结果火狐崩溃了,而且csdn居然没有自动保存,只有上午的有记录;想到这个安装的问题也折腾哥快一天了,还是再次打字把问题记录下吧,我就喜欢偏......
  • python处理word、ppt、excel
    介绍采用python_docx模块处理word文档的基本技巧,特别是图片如何提取和写入。 python_docx模块只能处理docx,不支持doc,如需使用,要进行转换。代码入下:fromwin32comimpor......
  • [读书笔记]Python编程:从入门到实践读后感
    0x00前言说句实在话,你买这本书根本就是一个错误。如果,你只是把它束之高阁,就认为自己学会了Python的话。诚如编辑所言,我自己买下这本书已经有一年多了,但真正把它读起来,......
  • Python 导入模块、文件、包、自定义路径包
    测试环境,假设:主文件绝对路径:/home/ubu/py_test/main.py模块文件:/home/ubu/py_test/con.py模块目录:/home/ubu/py_test/modules/tt.py模块目录:/home/ubu/py_test/modules......