191015-python的并发摸底

从前有个人,不会多线程。
因为自己写的小脚本太慢了,所以考虑上多线程。

1. Threading & Process

threadingprocess threading 看起来简单一些,因为变量啥的公用很方便,而且我的需求是就是让它发HTTP请求,回复的结果我也不会统一收集,所以就采用了产卵式多线程,来活了就干,也没有关心到底最多同时开了几个线程,机器是否吃得消。最简单的,官方文档:Thread,写好你的函数,每次新建一个thread 传递给target,然后一并传递你的目标函数的参数;然后遍历你的 原始数据 塞给一个那个Thread类就好。

def do(url, param):
   pass
def get_thread(url, param):
   new_thread=threading.Thread(target=do, args=(url,param))
   return new_thread

后来脚本发展到短时间内任务太多了,不能不考虑线程池了,不可能在短时间内迅速产生100个线程吧,主机上还有其他服务!

2.multiprocessing

多进程的我没有做,使用起来应该是大同小异,因为我看SOF上说python现在的设计方向就是API调用统一化;不过比较下多线程和多进程、协程之间各自的优缺点还是有必要[12345678];
这里就有人提问:https://stackoverflow.com/questions/3044580/multiprocessing-vs-threading-python
根据评论,多线程共享IO调度(因为资源是基于进程的),所以多线程并不适合IO密集型操作,也确实如此这里当时不知道看的哪个评论,完全得出来了相反的结论,也是奇怪,这两年我也没有从事python开发,但是现在看这些很容易看明白,我记得当时就是弄不明白,当时毕业才1年。
GIL:https://stackoverflow.com/questions/1294382/what-is-the-global-interpreter-lock-gil-in-cpython/55309364#55309364 经过时间的洗礼,这个问题人们选择了更好的回答:https://stackoverflow.com/a/1294402
因为Cpython的GIL的限制,一个python进程内只有一个线程占用解释器,也就是说实际上同时只能跑一个python线程,所以以为GIL,CPU密集型任务并不能充分利用多核计算机

  from multiprocessing import Pool
  from multiprocessing.pool import ThreadPool   # 等于 from   multiprocessing.dummy import Pool as ThreadPool

3. concurrent.futures & ThreadPoolExecutor ProcessPoolExecutor

concurrent.futures Executor ThreadPoolExecutorProcessPoolExecutor
concurrent.futures中对上面所说的pool进一步做了封装,几乎没啥区别。这里SOF上人家回答了很好,What’s the difference between python’s multiprocessing and concurrent.futures?
总结下这位的回答:
1.尽量使用 Executor
2.Excutor也可以使用底层multiprocessing.Pool中的一些工具方法
3.python3.5 以后,多进程的map效率和 Executor的一样了

在这里插入图片描述

 executor = ThreadPoolExecutor(max_workers=10)
 tasks = executor.submit(my_function)
 # 没写完,如果不是周期性循环处理,要考虑关闭线程池,同时要考虑timeout
 # or 推荐尽量使用map
 with ProcessPoolExecutor() as executor:
    result = executor.map(calculate, range(4))

这里有位仁兄验证了map的威力:Python 并行任务技巧 ,他的是IO密集型所以使用的是进程,其实我也是IO密集型…
对于Executor的使用细节,我还没有看文档,这里有人说了下,可以精读https://www.jianshu.com/p/b9b3d66aa0be ,我后面得自己看看了,怎样实现发出去的任务结果的处理,超时处理.

20231210
IO密集型可以用多线程,CPU密集型用多进程 ,多进程来绕开GIL 参见这里的测试: 什么是Python全局锁(GIL),如何避开GIL限制

4. subprocess和mulprocessing

简单来说,subprocess模块是用来调用系统程序或者命令,以控制输入或者捕获标准输出,
mulprocess是用来将你自己写的代码优化,采用多进程的方式执行。

引用Python文档:
https://docs.python.org/3/library/subprocess.html?highlight=subprocess

The subprocess module allows you to spawn new processes, connect to their input/output/error pipes, and obtain their return codes. 子进程模块允许你创建新的进程,连接他们的标准输入、标准输出、标准错误管道,并且获得他们的返回值。spawn(产卵;导致;引发;引起)

惯例SOF:
https://stackoverflow.com/questions/13606867/what-is-the-difference-between-multiprocessing-and-subprocess
同时需要注意到,在windows中运行subprocess.run()时需要指定参数shell=True,因为windows里面的命令不是真正的可执行程序

我才发现windows默认的照片查看器,在任务管理器中找不到,不是一个单独的可执行程序,如果需要使用命令执行:%SystemRoot%\System32\rundll32.exe "%ProgramFiles%\Windows Photo Viewer\PhotoViewer.dll", ImageView_Fullscreen %1%1就是照片路径,当然除了这个办法,我想到了start命令,完美解决使用命令打开照片的问题!
参见:https://www.sevenforums.com/software/180378-where-windows-photo-viewer-default-location.html
https://stackoverflow.com/questions/3022013/windows-cant-find-the-file-on-subprocess-call

2022.03.23当文档被翻译成中文后,顿觉非常简单
只是扫了一眼文档,没细看。像看小说一样看文档???
https://docs.python.org/zh-cn/3/library/multiprocessing.html

from multiprocessing import Pool

def f(x):
    return x*x

if __name__ == '__main__':
    with Pool(5) as p:
        print(p.map(f, [1, 2, 3]))

这个链接里还讲述了怎么创建进程,然后如何使用Queue进行对象交换、如何使用lock进行进程间同步,进程间如何共享
提一句共享里的Server Process与Manager,看了中文文档半天没理解服务进程是啥意思,这里翻译为服务器进程更好,如果是本地的Manager管理的对象和全局对象是没有什么区别的 ,Manager生成对象的特长就是可以通过socket共享。没有什么区别的 错误论断:搜索一番,实际上进程间根本不能共享全局变量(链接为示例)Proxy-Objects来进行共享,它是可以被序列化(python中为picklable)的
在windows中敲入Manager.list是会报错的:
An attempt has been made to start a new process before the current process has finished its bootstrapping phase.
multiprocessing.freeze_support()回答中大致说windows下没有fork系统调用,而是新启了一个进程进行模拟;把要处理的逻辑通过pipe序列化后传递过去。(其实这里还是没有搞懂和fork有啥关系)
没冻结的程序运行这个没有效果,如下:

Calling freeze_support() has no effect when invoked on any operating system other than Windows. In addition, if the module is being run normally by the Python interpreter on Windows (the program has not been frozen), then freeze_support() has no effect. https://docs.python.org/dev/library/multiprocessing.html#multiprocessing.freeze_support

一个Event的使用例子

5. greenlet 与协程

参考: https://www.jianshu.com/p/bb6c7f9aa1ae
https://greenlet.readthedocs.io/en/latest/
gevent是用greenlet实现的一个高效异步IO库:think of greenlets as cooperatively scheduled threads, 协作调度线程;greenlet就是python下的一个第三方协程库,不过现在python有了自己的协程
协程,看了些例子,感觉就是"线程"的上下文切换都交给应用来控制了
python协程coroutine https://www.geeksforgeeks.org/coroutine-in-python/

  • threads are preemptive and parallel,avoid race conditions, deadlocks 不过有时线程之间并不需要同步
    greenlets are cooperative and sequential, eliminate race conditions and greatly simplify the programming task

  • threads require resources from the operating system
    greenlets can require fewer resources

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值