Python之進程+線程+協程(multiprocessing多進程模塊)

前幾篇的多線程模塊的各種規則和用法,本篇則是關於多進程模塊的內容
1、multiprocessing的介紹
在Python中,由於有GIL解釋器鎖的存在,多線程就根本不是本質意義上的多線程,而是一個主線程我們通過控制子線程來實現的,本質上還是隻有一個線程,沒有並行效果。

但我們想要充分地使用多核CPU的資源,在Python中大部分情況都需要使用多進程。

multiprocessing包是Python中的多進程管理包。
與threading.Thread類似,它可以利用multiprocessing.Process對象來創建一個進程。
該進程可以運行在Python程序內部編寫的函數。該Process對象與Thread對象的用法相同,也有start(), run(), join()的方法。
此外multiprocessing包中也有Lock/Event/Semaphore/Condition類 (這些對象可以像多線程那樣,通過參數傳遞給各個進程),用以同步進程,其用法與threading包中的同名類一致。
所以,multiprocessing的很大一部份與threading使用同一套API,只不過換到了多進程的情境。

2、多進程的直接調用
(1)測試代碼:

from multiprocessing import Process  #導入多進程模塊
import time

def foo(name):
    time.sleep(1)
    print('Hello,', name,time.ctime())

if __name__ == '__main__':
    p_list=[]
    for i in range(3):
        #創建多進程對象,傳入函數名和函數需要的參數
        p = Process(target=foo, args=('Zahi',))

        #啓動多進程
        p_list.append(p)
        p.start()
        
    for i in p_list:
        p.join()
        
    print('---------------------end----------------------')

(2)測試結果:
1
可以看到,函數內是輸出語句根本沒有輸出到屏幕上,但是也沒有任何報錯語句,這是爲什麼呢?

本人反覆檢查代碼也沒有找出任何邏輯錯誤,後來通過查閱資料瞭解到,這是IDLE多進程的一個坑。

(3)print內容不顯示的原因:
spyder使用的stdout和windows不支持forking,所以無法打印子進程內容。

The comments revealed that OP uses Windows as well as Spyder. Since Spyder redirects stdoutand Windows does not support forking, a new child process won’t print into the Spyder console. This is simply due to the fact that stdout of the new child process is Python’s vanilla stdout, which can also be found in sys.stdout.

(4)兩種解決方法:
1.打印日誌到文件。2.用返回值替代打印。

import multiprocessing
 
def worker(num):
    """Returns the string of interest"""
    return "worker %d" % num
 
def main():
    pool = multiprocessing.Pool(4)
    results = pool.map(worker, range(10))
 
    pool.close()
    pool.join()
 
    for result in results:
        # prints the result string in the main process
        print(result)
 
if __name__ == '__main__':
    # Better protect your main function when you use multiprocessing
    main()

參考文章:【Python有坑系列】python多進程,函數內print的內容沒有打印出來

(5)改進後的代碼:

import multiprocessing  #多進程模塊
import time

def foo(n):
    time.sleep(1)
    return n,'-Hello,Zohi!',time.ctime()
    

if __name__ == '__main__':
    Li= []
    #實例化多進程對象
    p= multiprocessing.Pool(4)
    results= p.map(foo,range(5))
    
    p.close()  #關閉多進程
    p.join()

    for result in results:   
        print(result)
    


    print('---------------------end-------------------------')

(6)改進結果:
2
可以看到,五個進程同時運行,時間都精確到秒數了

3、繼承式調用


from multiprocessing import Process
import time

#繼承式調用多進程
class MyProcess(Process):
    def __init__(self,name):
        super(MyProcess, self).__init__()
        self.name = name

    def run(self):
        time.sleep(1)
        print('hello', self.name,time.ctime())


if __name__ == '__main__':
    p_list=[]
    for i in range(3):
        p = MyProcess('Zahi')
        p.start()
        
        p_list.append(p)

    for p in p_list:
        p.join()

    print('--------------------end---------------------')

4、Process下的方法
(1)構造方法:

Process([group [, target [, name [, args [, kwargs]]]]])
  group: 線程組,目前還沒有實現,庫引用中提示必須是None;
  target: 要執行的方法;
  name: 進程名;
  args/kwargs: 要傳入方法的參數。

(2)實例方法:

is_alive():返回進程是否在運行。
 join([timeout]):阻塞當前上下文環境的進程程,直到調用此方法的進程終止或到達指定的timeout(可選參數)。
 start():進程準備就緒,等待CPU調度
 run():strat()調用run方法,如果實例進程時未制定傳入target,這star執行t默認run()方法。
 terminate():不管任務是否完成,立即停止工作進程

(3)屬性:

daemon:和線程的setDeamon功能一樣
 name:進程名字。
 pid:進程號。

(4)練習代碼:

import time
from  multiprocessing import Process

def foo(i):
    time.sleep(1)
    return p.is_alive(),i,p.pid
    time.sleep(1)

if __name__ == '__main__':
    p_list=[]
    for i in range(10):
        p = Process(target=foo, args=(i,))
        #p.daemon=True  #相當於線程方法裏的setDaemon
        print(p)
        p_list.append(p)

    for p in p_list:
        p.start()
        
    for p in p_list:
        p.join()

    print('--------------------end----------------------')

3

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章