您的位置:首页 > 编程语言

21.番外篇:Tornado的多进程管理分析---process.py代码解读

2016-05-29 14:32 441 查看
Tornado的多进程管理我们可以参看process.py这个文件。

在编写多进程的时候我们一般都用python自带的multiprocessing,使用方法和threading基本一致,只需要继承里面的Process类以后就可以编写多进程程序了,这次我们看看tornado是如何实现他的multiprocessing,可以说实现的功能不多,但是更加简单高效。

我们只看fork_process里面的代码:

01
global
_task_id
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)
10
children
=
{}
这一段很简单,就是在没有传入进程数的时候使用默认的cpu个数作为将要生成的进程个数。

01
def
start_child(i):
02
pid
=
os.fork()
03
if
pid
=
=
0
:
04
#
child process
05
_reseed_random()
06
global
_task_id
07
_task_id
=
i
08
return
i
09
else
:
10
children[pid]
=
i
11
return
None
这是一个内函数,作用就是生成子进程。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):
2
id
=
start_child(i)
3
if
id
is
not
None
:
4
return
id
if id is not None表示如果我们在刚刚生成的那个子进程的上下文里面,那么就什么都不干,直接返回子进程的任务id就好了,啥都别想了,也别再折腾。如果还在父进程的上下文的话那么就继续生成子进程。

01
num_restarts
=
0
02
while
children:
03
try
:
04
pid,
status
=
os.wait()
05
except
OSError,
e:
06
if
e.errno
=
=
errno.EINTR:
07
continue
08
raise
09
if
pid
not
in
children:
10
continue
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))
18
else
:
19
logging.info(
"child
%d (pid%d) exited normally"
,
id
,
pid)
20
continue
21
num_restarts
+
=
1
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
:
26
return
new_id
剩下的这段代码都是在父进程里面做的事情(因为之前在子进程的上下文的时候已经返回了,当然子进程并没有结束)。

pid,status = os.wait()的意思是等待任意子进程退出或者结束,这时候我们就把它从我们的children表里面去除掉,然后通过status判断子进程退出的原因。

如果子进程是因为接收到kill信号或者抛出exception了,那么我们就重新启动一个子进程,用的当然还是刚刚退出的那个子进程的任务号。如果子进程是自己把事情做完了才退出的,那么就算了,等待别的子进程退出吧。

我们看到在重新启动子进程的时候又使用了

1
if
new_id
is
not
None
:
2
return
new_id
主要就是退出子进程的空间,只在父进程上面做剩下的事情,不然刚才父进程的那些代码在子进程里面也会同样的运行,就会形成无限循环了,我没试过,不如你试试?
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息