首頁 >後端開發 >Python教學 >理解python的多進程編程

理解python的多進程編程

高洛峰
高洛峰原創
2017-03-03 13:51:331238瀏覽

下面小編就為大家帶來一篇深入理解python多進程程式設計。小編覺得蠻不錯的,現在就分享給大家,也給大家做個參考。一起跟著小編過來看看吧

1、python多進程程式設計背景

python中的多進程最大的好處就是充分利用多核心cpu的資源,不像python中的多線程,受制於GIL的限制,從而只能進行cpu分配,在python的多進程中,適合於所有的場合,基本上能用多線程的,那麼基本上就能用多進程。

在進行多進程編程的時候,其實和多線程差不多,在多線程的包threading中,存在一個線程類Thread,在其中有三種方法來創建一個線程,啟動線程,其實在多在進程程式設計中,存在一個進程類別Process,也可以使用那集中方法來使用;在多執行緒中,記憶體中的資料是可以直接共享的,例如list等,但是在多進程中,記憶體資料是不能共享的,從而需要用單獨的資料結構來處理共享的資料;在多執行緒中,資料共享,要保證資料的正確性,從而必須要有所,但是在多進程中,鎖的考慮應該很少,因為進程是不共享記憶體資訊的,進程之間的交互資料必須經過特殊的資料結構,在多進程中,主要的內容如下圖:

理解python的多進程編程

##2、多行程的類別Process

多行程的類別Process和多執行緒的類別Thread差不多的方法,兩者的介面基本上相同,具體看以下的程式碼:

#!/usr/bin/env python

from multiprocessing import Process
import os
import time

def func(name):
  print 'start a process'
  time.sleep(3)
  print 'the process parent id :',os.getppid()
  print 'the process id is :',os.getpid()

if __name__ =='__main__':
  processes = []
  for i in range(2):
    p = Process(target=func,args=(i,))
    processes.append(p)
  for i in processes:
    i.start()
  print 'start all process'
  for i in processes:
    i.join()
    #pass
  print 'all sub process is done!'

在上面範例中可以看到,多進程和多執行緒的API介面是一樣一樣的,顯示建立進程,然後進行start開始運行,然後join等待進程結束。

在需要執行的函數中,印出了進程的id和pid,從而可以看到父進程和子進程的id號,在linu中,進程主要是使用fork出來的,在創建進程的時候可以查詢到父行程和子程序的id號,而在多執行緒中是無法找到執行緒的id,執行效果如下:

##
start all process
start a process
start a process

the process parent id : 8036
the process parent id : 8036
the process id is : 8037
the process id is : 8038
all sub process is done!

在作業系統中查詢的id的時候,最好用pstree,清晰:

├─sshd(1508)─┬─sshd(2259)───bash(2261)───python(7520)─┬─python(7521)
    │      │                    ├─python(7522)
    │      │                    ├─python(7523)
    │      │                    ├─python(7524)
    │      │                    ├─python(7525)
    │      │                    ├─python(7526)
    │      │                    ├─python(7527)
    │      │                    ├─python(7528)
    │      │                    ├─python(7529)
    │      │                    ├─python(7530)
    │      │                    ├─python(7531)
    │      │                    └─python(7532)

在進行運行的時候,可以看到,如果沒有join語句,那麼主進程是不會等待子進程結束的,是一直會執行下去,然後再等待子程序的執行。

在多進程的時候,說,我怎麼得到多進程的回傳值呢?然後寫了下面的程式碼:

#!/usr/bin/env python

import multiprocessing

class MyProcess(multiprocessing.Process):
  def __init__(self,name,func,args):
    super(MyProcess,self).__init__()
    self.name = name
    self.func = func
    self.args = args
    self.res = ''

  def run(self):
    self.res = self.func(*self.args)
    print self.name
    print self.res
    return (self.res,'kel')

def func(name):
  print 'start process...'
  return name.upper()

if __name__ == '__main__':
  processes = []
  result = []
  for i in range(3):
    p = MyProcess('process',func,('kel',))
    processes.append(p)
  for i in processes:
    i.start()
  for i in processes:
    i.join()
  for i in processes:
    result.append(i.res)
  for i in result:
    print i

嘗試從結果中傳回值,從而在主進程中得到子進程的回傳值,然而,,,並沒有結果,後來一想,在進程中,進程之間是不共享內存的,那麼使用list來存放數據顯然是不可行的,進程之間的交互必須依賴於特殊的數據結構,從而以上的代碼僅僅是執行進程,不能得到進程的回傳值,但是以上程式碼修改為線程,那麼是可以得到回傳值的。

3、進程間的互動Queue#進程間互動的時候,首先就可以使用在多執行緒裡面一樣的Queue結構,但在多重進程中,必須使用multiprocessing裡的Queue,程式碼如下:

#!/usr/bin/env python

import multiprocessing

class MyProcess(multiprocessing.Process):
  def __init__(self,name,func,args):
    super(MyProcess,self).__init__()
    self.name = name
    self.func = func
    self.args = args
    self.res = ''

  def run(self):
    self.res = self.func(*self.args)

def func(name,q):
  print 'start process...'
  q.put(name.upper())

if __name__ == '__main__':
  processes = []
  q = multiprocessing.Queue()
  for i in range(3):
    p = MyProcess('process',func,('kel',q))
    processes.append(p)
  for i in processes:
    i.start()
  for i in processes:
    i.join()
  while q.qsize() > 0:
    print q.get()

其實這個是上面範例的改進,在其中,並沒有使用什麼其他的程式碼,主要就是使用Queue來保存數據,因此可以達到進程間交換數據的目的。

在進行使用Queue的時候,其實用的是socket,感覺,因為在其中使用的還是發送send,然後是接收recv。

在進行資料交互的時候,其實是父行程和所有的子行程進行資料交互,所有的子行程之間基本上是沒有交互的,除非,但是,也是可以的,例如,每個進程去Queue中取數據,但這個時候應該要考慮鎖,不然可能會造成數據混亂。

4、 行程之間互動Pipe

在行程之間互動資料的時候還可以使用Pipe,程式碼如下:

#
#!/usr/bin/env python

import multiprocessing

class MyProcess(multiprocessing.Process):
  def __init__(self,name,func,args):
    super(MyProcess,self).__init__()
    self.name = name
    self.func = func
    self.args = args
    self.res = ''

  def run(self):
    self.res = self.func(*self.args)

def func(name,q):
  print 'start process...'
  child_conn.send(name.upper())

if __name__ == '__main__':
  processes = []
  parent_conn,child_conn = multiprocessing.Pipe()
  for i in range(3):
    p = MyProcess('process',func,('kel',child_conn))
    processes.append(p)
  for i in processes:
    i.start()
  for i in processes:
    i.join()
  for i in processes:
    print parent_conn.recv()

在上述程式碼中,主要是使用Pipe中傳回的兩個socket來進行傳輸和接收數據,在父進程中,使用的是parent_conn,在子進程中使用的是child_conn ,從而子程序發送資料的方法send,而在父進程中進行接收方法recv

最好的地方在於,明確的知道收發的次數,但是如果某個出現異常,那麼估計pipe不能使用了。

5、進程池pool其實在使用多進程的時候,感覺使用pool是最方便的,在多執行緒中是不存在pool的。

在使用pool的时候,可以限制每次的进程数,也就是剩余的进程是在排队,而只有在设定的数量的进程在运行,在默认的情况下,进程是cpu的个数,也就是根据multiprocessing.cpu_count()得出的结果。

在poo中,有两个方法,一个是map一个是imap,其实这两方法超级方便,在执行结束之后,可以得到每个进程的返回结果,但是缺点就是每次的时候,只能有一个参数,也就是在执行的函数中,最多是只有一个参数的,否则,需要使用组合参数的方法,代码如下所示:

#!/usr/bin/env python

import multiprocessing

def func(name):
  print 'start process'
  return name.upper()

if __name__ == '__main__':
  p = multiprocessing.Pool(5)
  print p.map(func,['kel','smile'])
  for i in p.imap(func,['kel','smile']):
    print i

在使用map的时候,直接返回的一个是一个list,从而这个list也就是函数执行的结果,而在imap中,返回的是一个由结果组成的迭代器,如果需要使用多个参数的话,那么估计需要*args,从而使用参数args。

在使用apply.async的时候,可以直接使用多个参数,如下所示:

#!/usr/bin/env python

import multiprocessing
import time
def func(name):
  print 'start process'
  time.sleep(2)
  return name.upper()

if __name__ == '__main__':
  results = []
  p = multiprocessing.Pool(5)
  for i in range(7):
    res = p.apply_async(func,args=('kel',))
    results.append(res)
  for i in results:
    print i.get(2.1)

在进行得到各个结果的时候,注意使用了一个list来进行append,要不然在得到结果get的时候会阻塞进程,从而将多进程编程了单进程,从而使用了一个list来存放相关的结果,在进行得到get数据的时候,可以设置超时时间,也就是get(timeout=5),这种设置。

总结:

在进行多进程编程的时候,注意进程之间的交互,在执行函数之后,如何得到执行函数的结果,可以使用特殊的数据结构,例如Queue或者Pipe或者其他,在使用pool的时候,可以直接得到结果,map和imap都是直接得到一个list和可迭代对象,而apply_async得到的结果需要用一个list装起来,然后得到每个结果。

以上这篇深入理解python多进程编程就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持PHP中文网。

更多理解python的多進程編程相关文章请关注PHP中文网!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn