1. 程式人生 > >python多程序--python之旅的首個坑

python多程序--python之旅的首個坑

注意這裡是多程序而不是多執行緒

一,先看多程序資料互動,使用Queue或Pipes這些multiprocessing模組封裝好的交換方式。

from multiprocessing import Process , Queue
import os, time, random

def write(q):
    print('write child process(%s) start..' % os.getpid())
    for i in ['你好', '大頭', '歌曲']:
        print('write child process(%s) write %s' % (os.getpid(),i))
        q.put(i)        #把資料寫入Queue
        time.sleep(random.random())
    print('write child process(%s) finish..' % os.getpid())

def read(q):
    print('read child process(%s) start..' % os.getpid())
    while True:
        data = q.get(True)  #讀取資料
        print('read child process(%s) read %s' % (os.getpid(),data))
        time.sleep(random.random())
    print('read child process(%s) finish..' % os.getpid())

if __name__ == '__main__':
    print('parent process(%s) start...' % os.getpid())
    q = Queue()  #只能用Queue,Pipes等來交換資料,如果試圖用list這種效果就和值傳遞一樣,起不了作用
    pw = Process(target=write, args=(q,))
    pr = Process(target=read, args=(q,))
    pw.start()
    pr.start()
    pw.join()
    time.sleep(7)      #父程序睡7秒保證,保證讀程序能讀完資料
    pr.terminate()    #讀寫成死迴圈,強行終止掉
    print('parent process(%s) end...' % os.getpid())

輸出:

parent process(2412) start...
write child process(3584) start..
write child process(3584) write 你好
read child process(13740) start..
read child process(13740) read 你好
write child process(3584) write 大頭
write child process(3584) write 歌曲
write child process(3584) finish..
read child process(13740) read 大頭
read child process(13740) read 歌曲
parent process(2412) end...

註釋已經寫的比較詳細了,可以看得出來,只需要用mulprocessing模組封裝好的交換方式作為引數傳遞給子程序,那麼這個引數就在這些程序中共享了,只需要查閱文件學習這些交換方式如何使用即可。

簡單記錄一下:

  • multiprocessing.Queue,是多程序安全的佇列(FIFO)
  • multiprocessing.Manager,封裝了可用於多程序的常用資料型別,例如list和dict
  • multiprocessing.Pipe,  是管道通訊

二,Pool, 提到多工肯定就離不開池的概念

池的想法很簡單,因為建立一個程序開銷大,所以可以先建立一堆程序,然後不關閉它們,重複利用這些程序,這就是程序池。同樣的道理,可以擴充套件到執行緒池,資料庫連線池,Http長連線等。

from multiprocessing import Pool
import os, random, time

def task(name):
    print('child process(%s) do task(%s) start...' % (os.gepid(),name))
    time.sleep(random.random())
    print('child process(%s) do task(%s) end...' % (os.gepid(), name))

if __name__ == '__main__':
    print('parent process(%s) start' % os.getpid())

    p = Pool(3) #程序池裡一共3個程序 如果不寫引數就是你的電腦執行緒數 比如6核12執行緒的cpu的話,那就預設12個
    for i in range(1,6):
        p.apply_async(task, args=(i,))  #向程序池中投放任務

    p.close()
    p.join()

    print('parent process(%s) end' % os.getpid())

Output:
parent process(6472) start
parent process(6472) end

一切都很簡單,然後可以清楚的看到子程序的print沒有列印。這裡終於遇到了python的首個坑,

經過一番查閱,得到的原因是:

這些子程序其實是成功執行了的,只不過由於,multiprocess封裝了fork,而windows又不支援fork(於是在windows上就自己模擬了fork,需要把物件都pickle序列化),這裡出了問題。或者可有可能是IDE的輸出不支援。

那麼解決方法有:

  • 換一個mac電腦
  • 裝一個Linux系統
  • 輸出子程序的日誌到檔案
  • 在夫程序中獲取子程序的返回值

三,一些問題和參考

1.可以注意到前面都有__name__ == '__main__'的判斷,如果不加為什麼會報錯?

參考:https://blog.csdn.net/llf_cloud/article/details/81775410

這是 Windows 上多程序的實現問題。在 Windows 上,子程序會自動 import 啟動它的這個檔案,而在 import 的時候是會執行這些語句的。如果你這麼寫的話就會無限遞迴建立子程序報錯。但是在multiprocessing.Process的原始碼中是對子程序再次產生子程序是做了限制的,是不允許的,於是出現如上的錯誤提示。所以必須把建立子程序的部分用那個 if 判斷保護起來,import 的時候 name 不是 main ,就不會遞迴運行了。
2.一篇比較詳細的文章

https://thief.one/2016/11/23/Python-multiprocessing/