Tornado的多进程管理我们可以参看process.py这个文件。
在编写多进程的时候我们一般都用python自带的multiprocessing,使用方法和threading基本一致,只需要继承里面的Process类以后就可以编写多进程程序了,这次我们看看tornado是如何实现他的multiprocessing,可以说实现的功能不多,但是更加简单高效。
我们只看fork_process里面的代码:
02 |
assert _task_id is None |
03 |
if num_processes is None or num_processes
< = 0 : |
04 |
num_processes = cpu_count() |
05 |
if ioloop.IOLoop.initialized(): |
06 |
raise RuntimeError( "Cannot
run in multiple processes: IOLoop instance " |
07 |
"has
already been initialized. You cannot call " |
08 |
"IOLoop.instance()
before calling start_processes()" ) |
09 |
logging.info( "Starting
%d processes" ,
num_processes) |
这一段很简单,就是在没有传入进程数的时候使用默认的cpu个数作为将要生成的进程个数。
这是一个内函数,作用就是生成子进程。fork是个很有意思的方法,他会同时返回两种状态,为什么呢?其实fork相当于在原有的一条路(父进程)旁边又修了一条路(子进程)。如果这条路修成功了,那么在原有的路上(父进程)你就看到旁边来了另外一条路(子进程),所以也就是返回新生成的那条路的名字(子进程的pid),但是在另外一条路上(子进程),你看到的是自己本身修建成功了,也就返回自己的状态码(返回结果是0)。
所以if pid==0表示这时候cpu已经切换到子进程了,相当于我们在新生成的这条路上面做事(返回任务id);else表示又跑到原来的路上做事了,在这里我们记录下新生成的子进程,这时候children[pid]=i里面的pid就是新生成的子进程的pid,而 i 就是刚才在子进程里面我们返回的任务id(其实就是用来代码子进程的id号)。
1 |
for i in range (num_processes): |
if id is not None表示如果我们在刚刚生成的那个子进程的上下文里面,那么就什么都不干,直接返回子进程的任务id就好了,啥都别想了,也别再折腾。如果还在父进程的上下文的话那么就继续生成子进程。
04 |
pid,
status = os.wait() |
06 |
if e.errno = = errno.EINTR: |
09 |
if pid not in children: |
11 |
id = children.pop(pid) |
12 |
if os.WIFSIGNALED(status): |
13 |
logging.warning( "child
%d (pid %d) killed by signal %d, restarting" , |
14 |
id ,
pid, os.WTERMSIG(status)) |
15 |
elif os.WEXITSTATUS(status)
! = 0 : |
16 |
logging.warning( "child
%d (pid %d) exited with status %d, restarting" , |
17 |
id ,
pid, os.WEXITSTATUS(status)) |
19 |
logging.info( "child
%d (pid %d) exited normally" , id ,
pid) |
22 |
if num_restarts
> max_restarts: |
23 |
raise RuntimeError( "Too
many child restarts, giving up" ) |
24 |
new_id = start_child( id ) |
25 |
if new_id is not None : |
剩下的这段代码都是在父进程里面做的事情(因为之前在子进程的上下文的时候已经返回了,当然子进程并没有结束)。
pid, status = os.wait()的意思是等待任意子进程退出或者结束,这时候我们就把它从我们的children表里面去除掉,然后通过status判断子进程退出的原因。
如果子进程是因为接收到kill信号或者抛出exception了,那么我们就重新启动一个子进程,用的当然还是刚刚退出的那个子进程的任务号。如果子进程是自己把事情做完了才退出的,那么就算了,等待别的子进程退出吧。
我们看到在重新启动子进程的时候又使用了
主要就是退出子进程的空间,只在父进程上面做剩下的事情,不然刚才父进程的那些代码在子进程里面也会同样的运行,就会形成无限循环了,我没试过,不如你试试?