解析Python下的多進程編程

來源:互聯網
上載者:User

解析Python下的多進程編程

   這篇文章主要介紹了初步解析Python下的多進程編程,使用多進程編程一直是Python編程當中的重點和痛點,需要的朋友可以參考下

  要讓Python程式實現多進程(multiprocessing),我們先瞭解作業系統的相關知識。

  Unix/Linux作業系統提供了一個fork()系統調用,它非常特殊。普通的函數調用,調用一次,返回一次,但是fork()調用一次,返回兩次,因為作業系統自動把當前進程(稱為父進程)複製了一份(稱為子進程),然後,分別在父進程和子進程內返回。

  子進程永遠返回0,而父進程返回子進程的ID。這樣做的理由是,一個父進程可以fork出很多子進程,所以,父進程要記下每個子進程的ID,而子進程只需要調用getppid()就可以拿到父進程的ID。

  Python的os模組封裝了常見的系統調用,其中就包括fork,可以在Python程式中輕鬆建立子進程:

  ?

1

2

3

4

5

6

7

8

9

# multiprocessing.py

import os

 

print 'Process (%s) start...' % os.getpid()

pid = os.fork()

if pid==0:

print 'I am child process (%s) and my parent is %s.' % (os.getpid(), os.getppid())

else:

print 'I (%s) just created a child process (%s).' % (os.getpid(), pid)

  運行結果如下:

  ?

1

2

3

Process (876) start...

I (876) just created a child process (877).

I am child process (877) and my parent is 876.

  由於Windows沒有fork調用,上面的代碼在Windows上無法運行。由於Mac系統是基於BSD(Unix的一種)核心,所以,在Mac下運行是沒有問題的,推薦大家用Mac學Python!

  有了fork調用,一個進程在接到新任務時就可以複製出一個子進程來處理新任務,常見的Apache伺服器就是由父進程監聽連接埠,每當有新的http請求時,就fork出子進程來處理新的http請求。

  multiprocessing

  如果你打算編寫多進程的服務程式,Unix/Linux無疑是正確的選擇。由於Windows沒有fork調用,難道在Windows上無法用Python編寫多進程的程式?

  由於Python是跨平台的,自然也應該提供一個跨平台的多進程支援。multiprocessing模組就是跨平台版本的多進程模組。

  multiprocessing模組提供了一個Process類來代表一個進程對象,下面的例子示範了啟動一個子進程並等待其結束:

  ?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

from multiprocessing import Process

import os

 

# 子進程要執行的代碼

def run_proc(name):

print 'Run child process %s (%s)...' % (name, os.getpid())

 

if __name__=='__main__':

print 'Parent process %s.' % os.getpid()

p = Process(target=run_proc, args=('test',))

print 'Process will start.'

p.start()

p.join()

print 'Process end.'

  執行結果如下:

  ?

1

2

3

4

Parent process 928.

Process will start.

Run child process test (929)...

Process end.

  建立子進程時,只需要傳入一個執行函數和函數的參數,建立一個Process執行個體,用start()方法啟動,這樣建立進程比fork()還要簡單。

  join()方法可以等待子進程結束後再繼續往下運行,通常用於進程間的同步。

  Pool

  如果要啟動大量的子進程,可以用進程池的方式大量建立子進程:

  ?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

from multiprocessing import Pool

import os, time, random

 

def long_time_task(name):

print 'Run task %s (%s)...' % (name, os.getpid())

start = time.time()

time.sleep(random.random() * 3)

end = time.time()

print 'Task %s runs %0.2f seconds.' % (name, (end - start))

 

if __name__=='__main__':

print 'Parent process %s.' % os.getpid()

p = Pool()

for i in range(5):

p.apply_async(long_time_task, args=(i,))

print 'Waiting for all subprocesses done...'

p.close()

p.join()

print 'All subprocesses done.'

  執行結果如下:

  ?

1

2

3

4

5

6

7

8

9

10

11

12

13

Parent process 669.

Waiting for all subprocesses done...

Run task 0 (671)...

Run task 1 (672)...

Run task 2 (673)...

Run task 3 (674)...

Task 2 runs 0.14 seconds.

Run task 4 (673)...

Task 1 runs 0.27 seconds.

Task 3 runs 0.86 seconds.

Task 0 runs 1.41 seconds.

Task 4 runs 1.91 seconds.

All subprocesses done.

  代碼解讀:

  對Pool對象調用join()方法會等待所有子進程執行完畢,調用join()之前必須先調用close(),調用close()之後就不能繼續添加新的Process了。

  請注意輸出的結果,task 0,1,2,3是立刻執行的,而task 4要等待前面某個task完成後才執行,這是因為Pool的預設大小在我的電腦上是4,因此,最多同時執行4個進程。這是Pool有意設計的限制,並不是作業系統的限制。如果改成:

  ?

1

p = Pool(5)

  就可以同時跑5個進程。

  由於Pool的預設大小是CPU的核心數,如果你不幸擁有8核CPU,你要提交至少9個子進程才能看到上面的等待效果。

  處理序間通訊

  Process之間肯定是需要通訊的,作業系統提供了很多機制來實現進程間的通訊。Python的multiprocessing模組封裝了底層的機制,提供了Queue、Pipes等多種方式來交換資料。

  我們以Queue為例,在父進程中建立兩個子進程,一個往Queue裡寫資料,一個從Queue裡讀資料:

  ?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

from multiprocessing import Process, Queue

import os, time, random

 

# 寫資料進程執行的代碼:

def write(q):

for value in ['A', 'B', 'C']:

print 'Put %s to queue...' % value

q.put(value)

time.sleep(random.random())

 

# 讀資料進程執行的代碼:

def read(q):

while True:

value = q.get(True)

print 'Get %s from queue.' % value

 

if __name__=='__main__':

# 父進程建立Queue,並傳給各個子進程:

q = Queue()

pw = Process(target=write, args=(q,))

pr = Process(target=read, args=(q,))

# 啟動子進程pw,寫入:

pw.start()

# 啟動子進程pr,讀取:

pr.start()

# 等待pw結束:

pw.join()

# pr進程裡是死迴圈,無法等待其結束,只能強行終止:

pr.terminate()

  運行結果如下:

  ?

1

2

3

4

5

6

Put A to queue...

Get A from queue.

Put B to queue...

Get B from queue.

Put C to queue...

Get C from queue.

  在Unix/Linux下,multiprocessing模組封裝了fork()調用,使我們不需要關注fork()的細節。由於Windows沒有fork調用,因此,multiprocessing需要“類比”出fork的效果,父進程所有Python對象都必須通過pickle序列化再傳到子進程去,所有,如果multiprocessing在Windows下調用失敗了,要先考慮是不是pickle失敗了。

  小結

  在Unix/Linux下,可以使用fork()調用實現多進程。

  要實現跨平台的多進程,可以使用multiprocessing模組。

  處理序間通訊是通過Queue、Pipes等實現的。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.