python 多线程,多进程,多协程

进程,线程,协程

如果你的代码是IO密集型的,线程和多进程可以帮到你。多进程比线程更易用,但是消耗更多的内存。如果你的代码是CPU密集型的,多进程就明显是更好的选择——特别是所使用的机器是多核或多CPU的。对于网络应用,在你需要扩展到多台机器上执行任务,RQ是更好的选择。
使用多进程,我们得建立一个多进程池。通过它提供的map方法,我们把URL列表传给池,然后8个新进程就会生成,它们将并行地去下载图片。这就是真正的并行,不过这是有代价的。整个脚本的内存将会被拷贝到各个子进程中。在我们的例子中这不算什么,但是在大型程序中它很容易导致严重的问题。
浅析Python中的多进程与多线程的使用

为什么在Python里推荐使用多进程而不是多线程

python多线程、多进程、协程的使用

关于python多线程编程中join()和setDaemon()的一点儿探究

进程池

创建进程需要消耗时间,销毁进程也需要时间。其次,即使开启了成千上万的进程,操作系统也不能让它们同时执行,这样反而会影响程序的效率。因此我们不能无限制的根据任务开启或者结束进程。

进程池:定义了一个池子,在里面放上固定数量的进程,有需求来了,就拿这个池中的一个进程来处理任务,等到处理完毕,进程并不关闭,而是将进程再放回进程池中继续等待认为。如果有许多任务需要执行,池中的进程数量不够,任务就要等待之前的进程执行任务完毕归来,拿到空闲进程才能继续执行。

对于python而言,由于其使用了全局解释锁(GIL),所以python的多线程并不能实现并发。因此,python的并发是通过多进程来实现的!!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import os
import time
import random
from multiprocessing import Pool
from multiprocessing import Process
def func(i):
i += 1
if __name__ == '__main__':
p = Pool(5) # 创建了5个进程
start = time.time()
p.map(func,range(1000))
p.close() # 是不允许再向进程池中添加任务
p.join()
print(time.time() - start) # 0.35544490814208984
start = time.time()
l = []
for i in range(1000):
p = Process(target=func,args=(i,)) # 创建了一百个进程
p.start()
l.append(p)
[i.join() for i in l]
print(time.time() - start) # 101.00088691711426

在写代码时,pool(3)之后程序好像死锁,搞了两天,终于知道是因为from gevent import monkey; monkey.patch_all();引入了这个,但并不知道原因

apply_async(func[,args[,kwds[,callback = None]]]) #非阻塞且支持结果返回进行回调

apply_async(函数,不定参数) #四个进程在Pool进程池内并行(分给4核cpu),剩下的交替进行.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from multiprocessing import Pool,Process
import time,os
def Foo(a):#创建函数
time.sleep(2)
print('in the process:',os.getpid(),os.getppid())
return a+100
def bar(arga):#创建函数
print('---->',arga)
if __name__ == '__main__':
print('父进程ID:',os.getpid())
pool = Pool(2)#创建进程池最大容量为2,最多允许2个进程同时运行,参照线程信号量模式
for i in range(10):#创建10个进程
pool.apply_async(func=Foo,args=(i,),callback=bar)#创建线程,参数1调用函数,参数2设置i为函数FOO参数,参数3为返回函数
#参数三为回调函数,将FOO函数的返回值作为参数调用bar函数。
#pool.apply(func=Foo,args=(i,),callback=bar)#串行
#pool.apply_async(func=Foo,args=(i,),callback=bar)#并行
pool.close()#关闭
#pool.join()#如果没有.join语句,程序不执行就会关闭。这里是个大坑。
  1. 对写入操作 进行加锁
    就是说:对当前进行写入操作的进程锁定,直到该进程写完十个字,也就是一个完成的操作,后解除该进程的锁定,放它离开。切换到另外一个进程来操作。
    因为一次只能一个进程执行写入操作,而且必须执行完成完整的操作,才允许切换。所以不会造成文件内容的混乱。
    但是加锁一般会完造成程序的执行效率下降。而且,如果写入操作分散在整个代码的多处,
    总不能把整个代码都锁起来吧,这样岂不是又成单进程了么?
    所以:把写入操作抽象出来为单独的一个函数,这样对单独的一个函数加群,这样问题就不大了。
    更优雅的方法:使用multiprocessing的回调函数。
    1。把写入操作抽象为单独的一个函数
    2。把进程需要写入的内容,作为返回值返回
    3。使用回调函数写入进程返回内容。

python多进程文件加锁与回调
[进程与线程,加锁](https://blog.csdn.net/u014265088/article/details/53128108)

python进程池:multiprocessing.pool

进程能否实例化

能否定义在类中的进程,这个目前还没有实验

Can’t pickle when using multiprocessing Pool.map()
另外找到的可能出错的原因:
Windows下面的multiprocessing跟Linux下面略有不同,Linux下面基于fork,fork之后所有的本地变量都复制一份,因此可以使用任意的全局变量;在Windows下面,多进程是通过启动新进程完成的,所有的全局变量都是重新初始化的,在运行过程中动态生成、修改过的全局变量是不能使用的。multiprocessing内部使用pickling传递map的参数到不同的进程,当传递一个函数或类时,pickling将函数或者类用所在模块+函数/类名的方式表示,如果对端的Python进程无法在对应的模块中找到相应的函数或者类,就会出错。当你在Interactive Console当中创建函数的时候,这个函数是动态添加到main模块中的,在重新启动的新进程当中不存在,所以会出错。当不在Console中,而是在独立Python文件中运行时,你会遇到另一个问题:由于你下面调用multiprocessing的代码没有保护,在新进程加载这个模块的时候会重新执行这段代码,创建出新的multiprocessing池,无限调用下去。解决这个问题的方法是永远把实际执行功能的代码加入到带保护的区域中:if name == ‘mian‘:

总结

pool 可以创建进程池
from functools import partial 引入多个参数
pool.map
pool.apply
pool.apply_aysnc
这些的区别在这片博客中:https://www.cnblogs.com/alan-babyblog/p/5351031.html

进程池

进程池报错可能中断运行,但不显示错误

坚持原创技术分享,您的支持将鼓励我继续创作!