徹底搞懂python執行multiprocessing Pool線程池程序報錯RuntimeError:reeze_support()

1.執行一個python的multiprocessing.Pool進程池程序,實現多進程程序,代碼如下,結果在windows下執行報錯,但是在linux和unix裏面執行沒有報錯?

from multiprocessing import  Pool
import  time ,os ,random

def worker(msg):
    t_start = time.time() #獲取當前系統時間,長整型,常用來測試程序執行時間
    print("%s開始執行,進程號爲%d" % (msg,os.getpid()))
    # random.random()隨機生成0~1之間的浮點數
    time.sleep(random.random()*2)
    t_stop = time.time()
    print(msg,"執行完畢,耗時%0.2f" % (t_stop-t_start))

po  = Pool(3)# 定義一個進程池,最大進程數3,大小可以自己設置,也可寫成processes=3
for i in range(0,10):
    # Pool().apply_async(要調用的目標,(傳遞給目標的參數元祖,))
    # 每次循環將會用空閒出來的子進程去調用目標
    po.apply_async(worker,(i,))

print("----start----")
po.close()  # 關閉進程池,關閉後po不再接收新的請求
po.join()  # 等待po中所有子進程執行完成,必須放在close語句之後
print("-----end-----")
'''
RuntimeError: 
        An attempt has been made to start a new process before the
        current process has finished its bootstrapping phase.

        This probably means that you are not using fork to start your
        child processes and you have forgotten to use the proper idiom
        in the main module:

            if __name__ == '__main__':
                freeze_support()
                ...

        The "freeze_support()" line can be omitted if the program
        is not going to be frozen to produce an executable.

'''

2.先解決問題

解決這個問題的方法很簡單,就是永遠把實際執行功能的代碼封裝成函數(不封裝直接放到保護區執行也可以),然後加入到帶保護的區域中:if __name__ == '__mian__':中執行。

from multiprocessing import  Pool
import  time ,os ,random


def worker(msg):
    t_start = time.time() #獲取當前系統時間,長整型,常用來測試程序執行時間
    print("%s開始執行,進程號爲%d" % (msg,os.getpid()))
    # random.random()隨機生成0~1之間的浮點數
    time.sleep(random.random()*2)
    t_stop = time.time()
    print(msg,"執行完畢,耗時%0.2f" % (t_stop-t_start))

def main():
    po  = Pool(3)# 定義一個進程池,最大進程數3,大小可以自己設置,也可寫成processes=3
    for i in range(0,10):
        # Pool().apply_async(要調用的目標,(傳遞給目標的參數元祖,))
        # 每次循環將會用空閒出來的子進程去調用目標
        po.apply_async(worker,(i,))

    print("----start----")
    po.close()  # 關閉進程池,關閉後po不再接收新的請求
    po.join()  # 等待po中所有子進程執行完成,必須放在close語句之後
    print("-----end-----")
if __name__ == '__main__':
    main()
'''
----start----
0開始執行,進程號爲5056
1開始執行,進程號爲968
2開始執行,進程號爲5448
2 執行完畢,耗時0.38
3開始執行,進程號爲5448
1 執行完畢,耗時0.47
4開始執行,進程號爲968
4 執行完畢,耗時0.02
5開始執行,進程號爲968
3 執行完畢,耗時0.13
6開始執行,進程號爲5448
5 執行完畢,耗時1.44
7開始執行,進程號爲968
6 執行完畢,耗時1.45
8開始執行,進程號爲5448
0 執行完畢,耗時1.99
9開始執行,進程號爲5056
8 執行完畢,耗時0.18
7 執行完畢,耗時0.58
9 執行完畢,耗時1.75
-----end-----'''

3.核心原因剖析

       前面案例我們發現使用多進程執行程序都沒有報錯,但是爲什麼使用 進程池就會報錯呢?那是因爲前面我們使用多線程執行時,把執行代碼封裝成了函數,放到了if __name__ == '__main__':中了哈哈哈。下面徹底具體分析一下原因,讓你徹底搞定原因。

1.弄明白這個問題,首先就先要明白python的執行過程與if __name__ == "__main__":

     一般的語言都是從main函數開始的。python有點不同,Python使用縮進對齊組織代碼的執行,所以所有沒有縮進的代碼(非函數定義和類定義),都會在載入時自動執行這些代碼,可以認爲是Python的main函數。

     python的主函數一般都寫成if __name__ == "__main__":當這個模塊是主調用的和被調用的時候,__name__的值是不一樣的,當這個模塊是主調用模塊的時候,__name__的值是"__main__",當這個模塊被調用的時候,__name__的值是這個模塊的名字。因此if __name__ == "__main__":這句話就可以起到主函數的作用。只有它是主調模塊的時候才執行,被別的模塊import的時候,就不會執行了。

      舉個簡單例子,abc.py裏面有if __name__ == "__main__":,當我們執行abc.py那麼這個mian就是主函數,但是如果我們在def.py中import abc時候,這個時候如果我們執行def.py,那麼abc.py中if __name__ == "__main__":就不會是程序入口main了,如果我們不調用abc.py執行的話,abc.py中的代碼是不會執行的。這個時候abc.py中的__name__的值就是模塊aaa的名字了。

2.什麼是multiprocessing?

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

     但是由於Windows沒有fork調用,所以爲了支持跨平臺,pytho搞了個跨平臺multiprocessing實現多進程,但是儘管如此在windows上和linux上,用multiprocessing實現方式還是不太一樣。在windows上會有一個import創建進程的模塊的操作,而linux上就沒有,基於fork。在windows上,子進程會自動import啓動它的這個文件,而在import的時候是會自動執行這些語句的(意思說子進程會複製並以import的形式導入執行主進程中代碼,如果你把要執行的代碼放到了if__name__ == "__main__"中,那麼這個時候因爲是import,所以這個時候就不會執行該代碼了)。所以創建進程的操作要用if __name__ == "__main__":保護起來,否則就會遞歸創建進程,或者出其它什麼錯誤。

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