一、简介
python多线程有个讨厌的限制,全局解释器锁(global interpreter lock),这个锁的意思是任一时间只能有一个线程使用解释器,跟单cpu跑多个程序一个意思,大家都是轮着用的,这叫“并发”,不是“并行”。手册上的解释是为了保证对象模型的正确性!这个锁造成的困扰是如果有一个计算密集型的线程占着cpu,其他的线程都得等着....,试想你的多个线程中有这么一个线程,得多悲剧,多线程生生被搞成串行;当然这个模块也不是毫无用处,手册上又说了:当用于IO密集型任务时,IO期间线程会释放解释器,这样别的线程就有机会使用解释器了!所以是否使用这个模块需要考虑面对的任务类型
python代码执行由python虚拟机(解释器主循环)来控制。对python虚拟机的访问由GIL控制,GIL保证同一时刻只有一个线程在执行。
python虚拟机执行过程:
1 2 3 4 5 6 7 8 9 10 11 |
|
由于GIL的限制,python多线程实际只能运行在单核CPU。如要实现多核CPU并行,只能充分地使用多核CPU的资源(os.cpu_count()查看,在python中大部分情况需要使用多进程。大部分并行模块中,多进程相当于开启多个python解释器,每个解释器对应一个进程。也有一些并行模块通过修改pyhton的GIL机制突破这个限制。
Python提供了multiprocessing。 multiprocessing模块用来开启子进程,并在子进程中执行我们定制的任务(比如函数),该模块与多线程模块threading的编程接口类似。
仔细说来, multiprocess不是一个模块而是python中一个操作、管理进程的包。之所以叫multi是取自multiple的多功能的意思,在这个包中几乎包含了和进程有关的所有子模块。由于提供的子模块非常多,为了方便大家归类记忆,我将这部分大致分为四个部分:创建进程部分,进程同步部分,进程池部分,进程之间数据共享。
二、multiprocessing.Process模块
Process模块是一个创建进程的模块, 借助这个模块, 就可以完成进程的创建。
与threading.Thread类似,它利用multiprocessing.Process对象来创建一个进程。
该进程可以运行在Python程序内部编写的函数。
该Process对象与Thread对象的用法相同,也有start(), run(), join()的方法。
此外multiprocessing包中也有Lock/Event/Semaphore/Condition类 (这些对象可以像多线程那样,通过参数传递给各个进程),用以同步进程,其用法与threading包中的同名类一致。
所以,multiprocessing的很大一部份与threading使用同一套API,只不过换到了多进程的情境。
但在使用这些共享API的时候,我们要注意以下几点:
1 2 3 4 5 6 7 8 9 |
|
使用格式:
1 2 3 4 5 |
|
参数介绍:
group为预留参数。 target为可调用对象(函数对象),为子进程对应的活动;相当于multiprocessing.Process子类化中重写的run()方法。 name为线程的名称,默认(None)为"Process-N"。 args、kwargs为进程活动(target)的非关键字参数、关键字参数。 deamon为bool值,表示是否为守护进程。
方法介绍:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
属性介绍
1 2 3 4 5 6 7 8 9 |
|
需要注意的是start(),join(),is_alive(), terminate()和exitcode方法只能由创建进程对象的过程调用。
三 Process类的使用
注意:在windows中Process()必须放到# if __name__ == '__main__':下
详细解释
1 2 3 4 5 6 7 8 9 |
|
创建并开启子进程的两种方式
方法一:multiprocessing.Process子类化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
方法二:通过函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
|
一些方法的示例用法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
|
在上述逻辑中,子进程会休息3s然后再打印一句话才结束,同时设定join(1)阻塞1s,阻塞在1s后结束,我们的并没有守护主进程,然后主进程结束后,子进程依然alive;
如果想要守护主进程,设定p.daemon = True:
1 2 3 4 5 6 7 8 9 10 |
|
在上述逻辑中,子进程会休息3s然后再打印一句话才结束,我们的设定守护主进程,然后主进程结束后,打印的is_alive: True这句话其实是在主进程里运行的,所以此时子进程确实是alive,但是主进程结束后子进程也结束了,不会运行info() 函数;
进程直接的内存空间是隔离的
1 2 3 4 5 6 7 8 9 10 11 12 |
|
对于要创建多个子进程的情形,更简洁的办法是采用进程池:multiprocessing.Pool(processes=None, initializer=None, initargs=(), maxtasksperchild=None)
1 2 3 4 |
|
如,将socket通信变成并发的形式
server.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
|
client.py
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
四、僵尸进程与孤儿进程(了解)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 |
|