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了,那麼我們就重新啓動一個子進程,用的當然還是剛剛退出的那個子進程的任務號。如果子進程是自己把事情做完了才退出的,那麼就算了,等待別的子進程退出吧。
我們看到在重新啓動子進程的時候又使用了
主要就是退出子進程的空間,只在父進程上面做剩下的事情,不然剛纔父進程的那些代碼在子進程裏面也會同樣的運行,就會形成無限循環了,我沒試過,不如你試試?