一、多进程

1.1 多进程的概念

  由于GIL的存在,python中的多线程其实并不是真正的多线程,如果想要充分地使用多核CPU的资源,在python中大部分情况需要使用多进程。Python提供了非常好用的多进程包multiprocessing,只需要定义一个函数,Python会完成其他所有事情。借助这个包,可以轻松完成从单进程到并发执行的转换。multiprocessing支持子进程、通信和共享数据、执行不同形式的同步,提供了Process、Queue、Pipe、Lock等组件。

  multiprocessing包是Python中的多进程管理包。与threading.Thread类似,它可以利用multiprocessing.Process对象来创建一个进程。该进程可以运行在Python程序内部编写的函数。该Process对象与Thread对象的用法相同,也有start(), run(), join()的方法。此外multiprocessing包中也有Lock/Event/Semaphore/Condition类 (这些对象可以像多线程那样,通过参数传递给各个进程),用以同步进程,其用法与threading包中的同名类一致。所以,multiprocessing的很大一部份与threading使用同一套API,只不过换到了多进程的情境。

但在使用这些共享API的时候,我们要注意以下几点:

  a、在UNIX平台上,当某个进程终结之后,该进程需要被其父进程调用wait,否则进程成为僵尸进程(Zombie)。所以,有必要对每个Process对象调用join()方法 (实际上等同于wait)。对于多线程来说,由于只有一个进程,所以不存在此必要性。

  b、Windows系统下,需要注意的是要想启动一个子进程,必须加上 if __name__ == "__main__",进程相关的要写在这句下面。

1.2 创建进程的两种方式

直接创建:

  1. from multiprocessing import Process
  2. import time
  3. def f(name):
  4. time.sleep(1)
  5. print('hello', name,time.ctime())
  6.  
  7. if __name__ == '__main__':
  8. p_list=[]
  9. for i in range(3):
  10. p = Process(target=f, args=('alvin',))
  11. p_list.append(p)
  12. p.start()
  13. for i in p_list:
  14. p.join()
  15. print('end')

Demo1

类式调用:

  1. from multiprocessing import Process
  2. import time
  3.  
  4. class MyProcess(Process):
  5. def __init__(self):
  6. super(MyProcess, self).__init__()
  7. #self.name = name
  8.  
  9. def run(self):
  10. time.sleep(1)
  11. print ('hello', self.name,time.ctime())
  12.  
  13. if __name__ == '__main__':
  14. p_list=[]
  15. for i in range(3):
  16. p = MyProcess()
  17. p.start()
  18. p_list.append(p)
  19.  
  20. for p in p_list:
  21. p.join()
  22.  
  23. print('end')

Demo2

二、Process类

2.1 构造方法

   Process([group [, target [, name [, args [, kwargs]]]]])

  group: 线程组,目前还没有实现,库引用中提示必须是None; 
  target: 要执行的方法; 
  name: 进程名; 
  args/kwargs: 要传入方法的参数。

2.2 实例方法

   is_alive():返回进程是否在运行。

  join([timeout]):阻塞当前上下文环境的进程程,直到调用此方法的进程终止或到达指定的timeout(可选参数)。

  start():进程准备就绪,等待CPU调度

  run():strat()调用run方法,如果实例进程时未制定传入target,这star执行t默认run()方法。

  terminate():不管任务是否完成,立即停止工作进程

2.3 属性

    authkey

  daemon:和线程的setDeamon功能一样

  exitcode(进程在运行时为None、如果为–N,表示被信号N结束)

  name:进程名字。

  pid:进程号。

  1. import time
  2. from multiprocessing import Process
  3.  
  4. def foo(i):
  5. time.sleep(1)
  6. print (p.is_alive(),i,p.pid)
  7. time.sleep(1)
  8.  
  9. if __name__ == '__main__':
  10. p_list=[]
  11. for i in range(10):
  12. p = Process(target=foo, args=(i,))
  13. #p.daemon=True
  14. p_list.append(p)
  15.  
  16. for p in p_list:
  17. p.start()
  18. # for p in p_list:
  19. # p.join()
  20.  
  21. print('main process end')

Demo

三、进程间通信

不同进程间内存是不共享的,要想实现两个进程间的数据交换,可以用以下方法:

3.1 Queues 使用方法跟threading里的queue类似 --> 将q作为参数传递给子进程。

  1. from multiprocessing import Process, Queue
  2.  
  3. def f(q,n):
  4. q.put([42, n, 'hello'])
  5.  
  6. if __name__ == '__main__':
  7. q = Queue()
  8. p_list=[]
  9. for i in range(3):
  10. p = Process(target=f, args=(q,i))
  11. p_list.append(p)
  12. p.start()
  13. print(q.get())
  14. print(q.get())
  15. print(q.get())
  16. for i in p_list:
  17. i.join()

Demo1

3.2 Pipes --> 通过管道Pipe实现。

  1. from multiprocessing import Process, Pipe
  2.  
  3. def f(conn):
  4. conn.send([42, None, 'hello'])
  5. conn.close()
  6.  
  7. if __name__ == '__main__':
  8. parent_conn, child_conn = Pipe()
  9. p = Process(target=f, args=(child_conn,))
  10. p.start()
  11. print(parent_conn.recv()) # prints "[42, None, 'hello']"
  12. p.join()

Demo2

3.3 数据共享(Manager)

Manager()返回的管理器对象控制一个服务器进程,该进程保存Python对象并允许其他进程使用代理操作它们。

  1. from multiprocessing import Process, Manager
  2.  
  3. def f(d, l,n):
  4. d[n] = ''
  5. d[''] = 2
  6. d[0.25] = None
  7. l.append(n)
  8. print(l)
  9.  
  10. if __name__ == '__main__':
  11. with Manager() as manager:
  12. d = manager.dict()
  13.  
  14. l = manager.list(range(5))
  15. p_list = []
  16. for i in range(10):
  17. p = Process(target=f, args=(d, l,i))
  18. p.start()
  19. p_list.append(p)
  20. for res in p_list:
  21. res.join()
  22.  
  23. print(d)
  24. print(l)

Demo

3.4 进程同步

Without using the lock output from the different processes is liable to get all mixed up.  如果不使用来自不同进程的锁定输出,则可能会混淆不清。

  1. from multiprocessing import Process, Lock
  2.  
  3. def f(l, i):
  4. l.acquire()
  5. try:
  6. print('hello world', i)
  7. finally:
  8. l.release()
  9.  
  10. if __name__ == '__main__':
  11. lock = Lock()
  12.  
  13. for num in range(10):
  14. Process(target=f, args=(lock, num)).start()

3.5 进程池

进程池内部维护一个进程序列,当使用时,则去进程池中获取一个进程,如果进程池序列中没有可供使用的进程,那么程序就会等待,直到进程池中有可用进程为止。

进程池中有两个方法:

  • apply
  • apply_async
  1. from multiprocessing import Process,Pool
  2. import time
  3.  
  4. def Foo(i):
  5. time.sleep(2)
  6. return i+100
  7.  
  8. def Bar(arg):
  9. print('-->exec done:',arg)
  10.  
  11. pool = Pool(5)
  12.  
  13. for i in range(10):
  14. pool.apply_async(func=Foo, args=(i,),callback=Bar)
  15. #pool.apply(func=Foo, args=(i,))
  16.  
  17. print('end')
  18. pool.close()
  19. pool.join()

四、协程

协程,又称微线程,纤程。英文名Coroutine。一句话说明什么是线程:协程是一种用户态的轻量级线程

协程拥有自己的寄存器上下文和栈。协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下文和栈。因此:

协程能保留上一次调用时的状态(即所有局部状态的一个特定组合),每次过程重入时,就相当于进入上一次调用的状态,换种说法:进入上一次离开时所处逻辑流的位置。

协程的好处:

  • 无需线程上下文切换的开销
  • 无需原子操作锁定及同步的开销
  • "原子操作(atomic operation)是不需要synchronized",所谓原子操作是指不会被线程调度机制打断的操作;这种操作一旦开始,就一直运行到结束,中间不会有任何 context switch (切换到另一个线程)。原子操作可以是一个步骤,也可以是多个操作步骤,但是其顺序是不可以被打乱,或者切割掉只执行部分。视作整体是原子性的核心。
  • 方便切换控制流,简化编程模型
  • 高并发+高扩展性+低成本:一个CPU支持上万的协程都不是问题。所以很适合用于高并发处理。

缺点:

  • 无法利用多核资源:协程的本质是个单线程,它不能同时将 单个CPU 的多个核用上,协程需要和进程配合才能运行在多CPU上.当然我们日常所编写的绝大部分应用都没有这个必要,除非是cpu密集型应用。
  • 进行阻塞(Blocking)操作(如IO时)会阻塞掉整个程序

4.1 使用yield实现协程操作

  1. 1 import time 
    2 import queue
    3 def consumer(name):
  2. print("--->starting eating baozi...")
  3. while True:
  4. new_baozi = yield
  5. print("[%s] is eating baozi %s" % (name,new_baozi))
  6. #time.sleep(1)
  7. def producer():
  8. r = con.__next__()
  9. r = con2.__next__()
  10. n = 0
  11. while n < 5:
  12. n +=1
  13. con.send(n)
  14. con2.send(n)
  15. print("\033[32;1m[producer]\033[0m is making baozi %s" %n )
  16. if __name__ == '__main__':
  17. con = consumer("c1")    # 创建生成器对象
  18. con2 = consumer("c2")    # 创建生成器对象
  19. p = producer()        # 执行producer函数

协程标准定义,即符合什么条件就能称之为协程:

  1. 必须在只有一个单线程里实现并发
  2. 修改共享数据不需加锁
  3. 用户程序里自己保存多个控制流的上下文栈
  4. 一个协程遇到IO操作自动切换到其它协程

基于上面这4点定义,我们刚才用yield实现的程并不能算是合格的线程,因为它有一点功能没实现。

4.2 Greenlet

greenlet是一个用C实现的协程模块,相比与python自带的yield,它可以使你在任意函数之间随意切换,而不需把这个函数先声明为generator

  1. # -*- coding:utf-8 -*-
  2.  
  3. from greenlet import greenlet
  4.  
  5. def test1():
  6. print(12)
  7. gr2.switch()
  8. print(34)
  9. gr2.switch()
  10.  
  11. def test2():
  12. print(56)
  13. gr1.switch()
  14. print(78)
  15.  
  16. gr1 = greenlet(test1)
  17. gr2 = greenlet(test2)
  18. gr1.switch()

感觉确实用着比generator还简单了,但好像还没有解决一个问题,就是遇到IO操作,自动切换,并不对。

4.3 Gevent

Gevent 是一个第三方库,可以轻松通过gevent实现并发同步或异步编程,在gevent中用到的主要模式是Greenlet, 它是以C扩展模块形式接入Python的轻量级协程。 Greenlet全部运行在主程序操作系统进程的内部,但它们被协作式地调度。

  1. import gevent
  2.  
  3. def func1():
  4. print('\033[31;1m李闯在跟海涛搞...\033[0m')
  5. gevent.sleep(2)
  6. print('\033[31;1m李闯又回去跟继续跟海涛搞...\033[0m')
  7.  
  8. def func2():
  9. print('\033[32;1m李闯切换到了跟海龙搞...\033[0m')
  10. gevent.sleep(1)
  11. print('\033[32;1m李闯搞完了海涛,回来继续跟海龙搞...\033[0m')
  12.  
  13. gevent.joinall([
  14. gevent.spawn(func1),
  15. gevent.spawn(func2),
  16. #gevent.spawn(func3),
  17. ])

输出:

李闯在跟海涛搞...
李闯切换到了跟海龙搞...
李闯搞完了海涛,回来继续跟海龙搞...
李闯又回去跟继续跟海涛搞...

五、补充

5.1 同步与异步的性能区别

  1. import gevent
  2.  
  3. def task(pid):
  4. """
  5. Some non-deterministic task
  6. """
  7. gevent.sleep(0.5)
  8. print('Task %s done' % pid)
  9.  
  10. def synchronous():
  11. for i in range(1,10):
  12. task(i)
  13.  
  14. def asynchronous():
  15. threads = [gevent.spawn(task, i) for i in range(10)]
  16. gevent.joinall(threads)
  17.  
  18. print('Synchronous:')
  19. synchronous()
  20.  
  21. print('Asynchronous:')
  22. asynchronous()

上面程序的重要部分是将task函数封装到Greenlet内部线程的gevent.spawn。 初始化的greenlet列表存放在数组threads中,此数组被传给gevent.joinall 函数,后者阻塞当前流程,并执行所有给定的greenlet。执行流程只会在 所有greenlet执行完后才会继续向下走。

5.2 遇到IO阻塞时会自动切换任务(gevent 库中的 monkey 方法)--> 补丁

  ------->能够最大程度监听IO阻塞,提高效率。

  1. from gevent import monkey
  2. monkey.patch_all()
  3.  
  4. import gevent
  5. from urllib.request import urlopen
  6.  
  7. def f(url):
  8. print('GET: %s' % url)
  9. resp = urlopen(url)
  10. data = resp.read()
  11. print('%d bytes received from %s.' % (len(data), url))
  12.  
  13. gevent.joinall([
  14. gevent.spawn(f, 'https://www.python.org/'),
  15. gevent.spawn(f, 'https://www.yahoo.com/'),
  16. gevent.spawn(f, 'https://github.com/'),
  17. ])

多进程(multiprocessing module)的更多相关文章

  1. python 3 并发编程之多进程 multiprocessing模块

    一 .multiprocessing模块介绍 python中的多线程无法利用多核优势,如果想要充分地使用多核CPU的资源(os.cpu_count()查看),在python中大部分情况需要使用多进程. ...

  2. 操作系统OS,Python - 多进程(multiprocessing)、多线程(multithreading)

    多进程(multiprocessing) 参考: https://docs.python.org/3.6/library/multiprocessing.html 1. 多进程概念 multiproc ...

  3. 多进程Multiprocessing模块

    多进程 Multiprocessing 模块 先看看下面的几个方法: star() 方法启动进程, join() 方法实现进程间的同步,等待所有进程退出. close() 用来阻止多余的进程涌入进程池 ...

  4. 13、多进程multiprocessing、进程池

    内容相关: multiprocessing: 进程的创建与运行 进程常用相关函数 进程池: 为什么要有进程池 进程池的创建与运行:串行.并行 回调函数 多进程multiprocessing: pyth ...

  5. python ---多进程 Multiprocessing

    和 threading 的比较 多进程 Multiprocessing 和多线程 threading 类似, 他们都是在 python 中用来并行运算的. 不过既然有了 threading, 为什么 ...

  6. Httpd服务入门知识-Httpd服务常见配置案例之MPM( Multi-Processing Module)多路处理模块

    Httpd服务入门知识-Httpd服务常见配置案例之MPM( Multi-Processing Module)多路处理模块 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任.   一.M ...

  7. Python 多进程 multiprocessing.Pool类详解

    Python 多进程 multiprocessing.Pool类详解 https://blog.csdn.net/SeeTheWorld518/article/details/49639651

  8. python中多进程multiprocessing、多线程threading、线程池threadpool

    浅显点理解:进程就是一个程序,里面的线程就是用来干活的,,,进程大,线程小 一.多线程threading 简单的单线程和多线程运行:一个参数时,后面要加逗号 步骤:for循环,相当于多个线程——t=t ...

  9. 物无定味适口者珍,Python3并发场景(CPU密集/IO密集)任务的并发方式的场景抉择(多线程threading/多进程multiprocessing/协程asyncio)

    原文转载自「刘悦的技术博客」https://v3u.cn/a_id_221 一般情况下,大家对Python原生的并发/并行工作方式:进程.线程和协程的关系与区别都能讲清楚.甚至具体的对象名称.内置方法 ...

随机推荐

  1. matlab 摘要

    matlab中的向量与矩阵 如果定义一个A = [1, 2, 3]; 则A为一个行向量 但是A(:)返回的是一个列向量 关于函数的返回值 在function [a, b, c] = fit_quadr ...

  2. LCS与打印路径

    /* LCS */ #include<bits/stdc++.h> using namespace std; const int maxn = 1000; int dp[maxn][max ...

  3. mysql关闭严格模式

    通过配置文件修改: linux找my.cnf文件 window的修改办法是找my.ini sql_mode=NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES  ub ...

  4. [转]在离线环境中发布.NET Core至Windows Server 2008

    本文转自:http://www.cnblogs.com/durow/p/5765145.html 0x00 写在开始 之前一篇博客中写了在离线环境中使用.NET Core,之后一边学习一边写了一些页面 ...

  5. c#真正判断文件类型

    //真正判断文件类型的关键函数 public static bool IsAllowedExtension2(FileUpload hifile) { if (hifile != null) { Sy ...

  6. Linux 作业调度器 crond

    linux缺省会启动crond进程,crond进程不需要用户启动.关闭.  需要启动/关闭cron /sbin/service crond start --启动服务 /sbin/service cro ...

  7. css 引入方式以及css的选择器

    一.css的引入方式: 1.行内样式 <div> <p style="color: red">我是一个段落</p> </div> 2 ...

  8. MotionEvent的getX(),getY()与getRawX(),getRawY()区别

    在Android的View中getX  getRawX获取的坐标是不一样的,只是相对比照的对象不一样而已. 1.在View中: getX()是表示Widget相对于自身左上角的x坐标,而getRawX ...

  9. Unity利用AnimationCurve做物体的各种运动

    ​之前一直都是自己学习Unity各种做Demo,最近开始正式使用Unity来做一个款2d的游戏. 其中在做一个类似小球弹跳运动的时候遇到了点问题,查找了很多资料,无意间发现AnimationCurve ...

  10. centos7 & centos6 rrdcache

    cat > /etc/systemd/system/rrdcached.service << EOF [Unit] Description=Data caching daemon f ...