### 在Python中多进程的创建方式对比:
1. 在Python中,可以通过`os.fork()`创建子进程,但是这种方式只能在`linux`和`unix`以及`mac`下面使用,不能跨平台,所以一般不推荐使用这种方式。
2. 使用`multiprocessing`模块也可以创建多进程,并且这种方式在写起来更加简单,并且支持跨平台。所以一般推荐使用`multiprocessing`的方式来写多进程的代码。
### `multiprocessing`的基本用法:
`multiprocessing`这个模块下面有一个`Process`的类。使用这个类可以创建一个多进程,使用方式如下:
```python
from multiprocessing import Process
import time
def zhiliao():
print('zhiliao process')
if __name__ == '__main__':
# 开启一个子进程
p = Process(target=zhiliao)
p.start()
while True:
print('main process')
time.sleep(1)
```
需要注意一点的是,如果在`windows`操作系统下,所有和进程创建相关的代码都必须放在`__name__=='__main__'`下面,否则会报错。
### 获取进程号:
1. 通过`os.getpid()`可以获取到当前这个进程的id。
2. 通过`os.getppid()`可以获取到当前这个进程的父进程的id。
### 父进程会等待所有子进程执行完毕后在退出:
如果在父进程中执行完所有代码后,还有子进程在执行,那么父进程会等待子进程执行完所有代码后再退出。
### `join`方法:
`join`方法可以让你的主进程阻塞,知道这个子进程执行完毕以后才会执行主进程后面的代码。
```python
def zhiliao():
for x in range(5):
print('子进程:%s' % x)
time.sleep(1)
if __name__ == '__main__':
p = Process(target=zhiliao)
p.start()
print('主进程')
# join方法的时候,就相当于主进程会阻塞在这个地方
# 直到这个子进程执行完毕以后才会执行父进程后的代码
p.join(3)
print('所有子进程都执行完毕')
```
### 使用类的方式创建子进程:
1. 使用`Process`作为父类,重新自定义一个类。
2. 在自定义的类中,重写父类的`run`方法,这个是必须的。其他方法,就按照你平时如何写就可以了。
3. 使用自定义的进程类创建子进程的时候,不需要传`target`参数。
```python
class MyProcess(Process):
def run(self):
print('子进程的id:%s' % os.getpid())
print('父进程的id:%s' % os.getppid())
for x in range(5):
print('子进程:%s' % x)
if __name__ == '__main__':
p = MyProcess()
p.start()
print('父进程的id:%s' % os.getpid())
print('子进程开始了')
p.join()
print('子进程执行完毕')
```
### 进程池:
1. `multiprocessing`中的`Pool`可以实现一个容器,来管理子进程。
2. 使用进程池有什么好处:进程池可以控制同一时刻,最多只能有多少个进程在运行。
3. 主进程不会等待进程池中的子进程都执行完毕以后再退出。而是如果父进程代码执行完毕以后,就会将整个程序都退出,所以我们在写进程池的时候,应该使用`pool.join()`来保证进程池中所有的子进程都能够执行完成。
4. `apply_async`相当于是并联的方式执行(同一时刻可以执行多个任务)。`apply`相当于是串联的方式执行(同一时刻只能执行一个任务,并且只能等待前面的任务执行完后才能执行后面的任务)。
```python
from multiprocessing import Process,Pool
import os
import time
def worker(num):
for x in range(5):
print('num:%s,pid:%s' % (num, os.getpid()))
time.sleep(1)
if __name__ == '__main__':
# 这个池子中同一时刻最多只能有3个进程
pool = Pool(3)
for x in range(10):
pool.apply_async(worker,(x,))
# 关闭进程池,不能再添加新进程了
pool.close()
# 主进程把子进程添加到进程池中后,不会等待进程池中其他的子进程都执行完毕后再退出,
# 而是当主进程的代码执行完毕后会立刻退出,因此如果这个地方没有join,那么子进程
# 将得不到执行
pool.join()
```
### 进程间数据不共享:
在程序中创建了子进程,子进程会完全copy一份主进程的环境,包括变量、函数、类等。
所以在子进程中使用变量、函数等的时候,其实是使用的是子进程中的那一份。跟主进程没有任何关系。
### `Queue`消息队列:
1. Queue(n):初始化一个消息队列,并指定这个队列中最多能够容纳多少条消息。
2. put(obj,[block[,timeout]]):推入一条消息到这个队列中。默认是阻塞的,也就是说如果这个消息队列中已经满了,那么会会一直等待,将这个消息添加到消息队列中。timeout可以指定这个阻塞最长的时间,如果超过这个时间还是满的,就会抛出异常。
3. put_nowait() :非阻塞的推入一条消息,如果这个队列已经满了,那么会立马抛出异常。其实这个方法等价于`put(block=False)`。
4. qsize():获取这个消息队列消息的数量。
5. full():判断这个消息队列是满否了。
6. empty():判断这个消息队列是否空了。
7. get([block[,timeout]]):获取队列中的一条消息,然后将其从队列中移除,block默认为True。如果设置block为False,那么如果没值,会立马抛出异常。timeout指定如果多久没有获取到值后会抛出异常。他获取值的方式是以先进先出的方式获取的。不要被队列两个字误解了。
### 使用`Queue`做进程间通信:
1. 给`Process`进程做通信:直接使用`Queue`的对象作为进程的参数就可以了。
2. 给`Pool`进程做通信,应该使用`multiprocessing.Manager().Queue()`对象来做通信,这个对象的使用方法跟`multiprocessing.Queue()`是一样的。
网友评论