IOTXING

记录技术学习之路

0%

Python 进程详解

Python 进程

因为GIL的限制,一个进程中只能有一个线程获取到资源,因此在有些场景下会显得很鸡肋。一种解决方法就是利用多进程,通过开多个进程就可以绕开GIL的限制。

多进程

python提供了一个包,multiprocess ,使用方法跟多线程的一样

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
    from multiprocessing import Process

plist = []

def addProcess(i):
plist.append(i)
print 'process %d ----'%i
print plist

for i in range(5):
p = Process(target=addProcess,args=(i,))
p.start()
```

结果
process 0 ----
[0]
process 1 ----
[1]
process 2 ----
[2]
process 3 ----
[3]
process 4 ----
[4]
1
2
3
4
5
6
7
8

从结果上面我们能看出来,每个进程都有自己独立的一片数据空间,不会受到其它进程的影响。

多进程的数据共享

虽然多进程能够开辟多个数据空间,但是这些数据空间的数据确实独立的,无法进行共享。因此很多场景下并不能符合我们的要求。好在我们可以通过别的方式进行资源的共享,例如Queue,Array和Manager这三个类来实现功能

Array
from multiprocessing import Process
from multiprocessing import Array
plist = Array('i',10)

def addProcess(i):
    plist[0] += 200
    print 'process %d ----'%i
    print [item for item in plist]

for i in range(5):
    p = Process(target=addProcess,args=(i,))
    p.start()

process 0 ----
[200, 0, 0, 0, 0, 0, 0, 0, 0, 0]
process 1 ----
[400, 0, 0, 0, 0, 0, 0, 0, 0, 0]
process 2 ----
[600, 0, 0, 0, 0, 0, 0, 0, 0, 0]
process 3 ----
[800, 0, 0, 0, 0, 0, 0, 0, 0, 0]
process 4 ----
[1000, 0, 0, 0, 0, 0, 0, 0, 0, 0]
1
2
3
4

这里我们使用的是multiprocess提供的ArrayArray初始化的时候至少传入两个参数,第一个参数为元素类型,这个指的是内部全部元素的类型,第二个参数为初始值或者长度

源码
def Array(typecode_or_type, size_or_initializer, **kwds):
    '''
    Returns a synchronized shared array
    '''
    from multiprocessing.sharedctypes import Array
    return Array(typecode_or_type, size_or_initializer, **kwds)
1
2

元素类型对应表
‘c’: ctypes.c_char, ‘u’: ctypes.c_wchar,
‘b’: ctypes.c_byte, ‘B’: ctypes.c_ubyte,
‘h’: ctypes.c_short, ‘H’: ctypes.c_ushort,
‘i’: ctypes.c_int, ‘I’: ctypes.c_uint,
‘l’: ctypes.c_long, ‘L’: ctypes.c_ulong,
‘f’: ctypes.c_float, ‘d’: ctypes.c_double
1
2
3
4
5
6
7
8

Queue

这里的队列就跟多线程中的队列用法是一样的,但是如果不加锁的话,也是会出现一些脏数据的情况

Manager

源码
def Manager():
    '''
    Returns a manager associated with a running server process

    The managers methods such as `Lock()`, `Condition()` and `Queue()`
    can be used to create shared objects.
    '''
    from multiprocessing.managers import SyncManager
    m = SyncManager()
    m.start()
    return m
1
2
3
4
5
6
7
8
9
10
11
12

实例

[http://www.jb51.net/article/57663.htm](http://www.jb51.net/article/57663.htm)

进程锁

multiprocess中实现了跟多线程中一样的锁,如Lock,Rlock,SEMAPHORE,EVENT,CONDITION [http://www.iotxing.com/2018/05/13/159/python-thread/](http://www.iotxing.com/2018/05/13/159/python-thread/)

进程池

既然有线程池,那必然也有进程池。但是,python给我们内置了一个进程池,不需要像线程池那样需要自定义,你只需要简单的from multiprocessing import Pool。
# -*- coding:utf-8 -*-
from multiprocessing import Pool
import time

def f1(args):
    time.sleep(1)
    print(args)

if __name__ == '__main__':
    p = Pool(5)
    for i in range(30):
        p.apply_async(func=f1, args= (i,))
    p.close()           # 等子进程执行完毕后关闭进程池
    # time.sleep(2)
    # p.terminate()     # 立刻关闭进程池
    p.join()

```

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

进程池中有以下几个主要方法:

apply:从进程池里取一个进程并执行

apply_async:apply的异步版本

terminate:立刻关闭进程池

join:主进程等待所有子进程执行完毕。必须在close或terminate之后。

close:等待所有进程结束后,才关闭进程池。