解釋見 https://blog.csdn.net/qq_26442553/article/details/94595715
原文如下:
1.執(zhí)行一個(gè)python的multiprocessing.Pool進(jìn)程池程序,實(shí)現(xiàn)多進(jìn)程程序,代碼如下,結(jié)果在windows下執(zhí)行報(bào)錯,但是在linux和unix里面執(zhí)行沒有報(bào)錯?
from multiprocessing import? Pool
import? time ,os ,random
def worker(msg):
? ? t_start = time.time() #獲取當(dāng)前系統(tǒng)時(shí)間,長整型,常用來測試程序執(zhí)行時(shí)間
? ? print("%s開始執(zhí)行,進(jìn)程號為%d" % (msg,os.getpid()))
? ? # random.random()隨機(jī)生成0~1之間的浮點(diǎn)數(shù)
? ? time.sleep(random.random()*2)
? ? t_stop = time.time()
? ? print(msg,"執(zhí)行完畢,耗時(shí)%0.2f" % (t_stop-t_start))
po? = Pool(3)# 定義一個(gè)進(jìn)程池,最大進(jìn)程數(shù)3,大小可以自己設(shè)置,也可寫成processes=3
for i in range(0,10):
? ? # Pool().apply_async(要調(diào)用的目標(biāo),(傳遞給目標(biāo)的參數(shù)元祖,))
? ? # 每次循環(huán)將會用空閑出來的子進(jìn)程去調(diào)用目標(biāo)
? ? po.apply_async(worker,(i,))
print("----start----")
po.close()? # 關(guān)閉進(jìn)程池,關(guān)閉后po不再接收新的請求
po.join()? # 等待po中所有子進(jìn)程執(zhí)行完成,必須放在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.先解決問題
解決這個(gè)問題的方法很簡單,就是永遠(yuǎn)把實(shí)際執(zhí)行功能的代碼封裝成函數(shù)(不封裝直接放到main中執(zhí)行也可以),然后加入到主函數(shù):if __name__ == '__main__':中執(zhí)行。
from multiprocessing import? Pool
import? time ,os ,random
def worker(msg):
? ? t_start = time.time() #獲取當(dāng)前系統(tǒng)時(shí)間,長整型,常用來測試程序執(zhí)行時(shí)間
? ? print("%s開始執(zhí)行,進(jìn)程號為%d" % (msg,os.getpid()))
? ? # random.random()隨機(jī)生成0~1之間的浮點(diǎn)數(shù)
? ? time.sleep(random.random()*2)
? ? t_stop = time.time()
? ? print(msg,"執(zhí)行完畢,耗時(shí)%0.2f" % (t_stop-t_start))
def main():
? ? po? = Pool(3)# 定義一個(gè)進(jìn)程池,最大進(jìn)程數(shù)3,大小可以自己設(shè)置,也可寫成processes=3
? ? for i in range(0,10):
? ? ? ? # Pool().apply_async(要調(diào)用的目標(biāo),(傳遞給目標(biāo)的參數(shù)元祖,))
? ? ? ? # 每次循環(huán)將會用空閑出來的子進(jìn)程去調(diào)用目標(biāo)
? ? ? ? po.apply_async(worker,(i,))
? ? print("----start----")
? ? po.close()? # 關(guān)閉進(jìn)程池,關(guān)閉后po不再接收新的請求
? ? po.join()? # 等待po中所有子進(jìn)程執(zhí)行完成,必須放在close語句之后
? ? print("-----end-----")
if __name__ == '__main__':
? ? main()
'''
----start----
0開始執(zhí)行,進(jìn)程號為5056
1開始執(zhí)行,進(jìn)程號為968
2開始執(zhí)行,進(jìn)程號為5448
2 執(zhí)行完畢,耗時(shí)0.38
3開始執(zhí)行,進(jìn)程號為5448
1 執(zhí)行完畢,耗時(shí)0.47
4開始執(zhí)行,進(jìn)程號為968
4 執(zhí)行完畢,耗時(shí)0.02
5開始執(zhí)行,進(jìn)程號為968
3 執(zhí)行完畢,耗時(shí)0.13
6開始執(zhí)行,進(jìn)程號為5448
5 執(zhí)行完畢,耗時(shí)1.44
7開始執(zhí)行,進(jìn)程號為968
6 執(zhí)行完畢,耗時(shí)1.45
8開始執(zhí)行,進(jìn)程號為5448
0 執(zhí)行完畢,耗時(shí)1.99
9開始執(zhí)行,進(jìn)程號為5056
8 執(zhí)行完畢,耗時(shí)0.18
7 執(zhí)行完畢,耗時(shí)0.58
9 執(zhí)行完畢,耗時(shí)1.75
-----end-----'''
3.核心原因剖析
? ? ? 前面案例我們發(fā)現(xiàn)使用多進(jìn)程執(zhí)行程序都沒有報(bào)錯,但是為什么使用 進(jìn)程池就會報(bào)錯呢?那是因?yàn)榍懊嫖覀兪褂枚嗑€程執(zhí)行時(shí),把執(zhí)行代碼封裝成了函數(shù),放到了if __name__ == '__main__':中了哈哈哈。下面徹底具體分析一下原因,讓你徹底搞定原因。
1.弄明白這個(gè)問題,首先就先要明白python的執(zhí)行過程與if __name__ == "__main__":
? ? 一般的語言都是從main函數(shù)開始的。python有點(diǎn)不同,Python使用縮進(jìn)對齊組織代碼的執(zhí)行,所以所有沒有縮進(jìn)的代碼(非函數(shù)定義和類定義),都會在載入時(shí)自動執(zhí)行,這些代碼,可以認(rèn)為是Python的main函數(shù)。
? ? python的主函數(shù)一般都寫成if __name__ == "__main__":當(dāng)這個(gè)模塊是主調(diào)用的和被調(diào)用的時(shí)候,__name__的值是不一樣的,當(dāng)這個(gè)模塊是主調(diào)用模塊的時(shí)候,__name__的值是"__main__",當(dāng)這個(gè)模塊被調(diào)用的時(shí)候,__name__的值是這個(gè)模塊的名字。因此if __name__ == "__main__":這句話就可以起到主函數(shù)的作用。只有它是主調(diào)模塊的時(shí)候才執(zhí)行,被別的模塊import的時(shí)候,就不會執(zhí)行了。
? ? ? 舉個(gè)簡單例子,abc.py里面有if __name__ == "__main__":,當(dāng)我們執(zhí)行abc.py那么這個(gè)mian就是主函數(shù),但是如果我們在def.py中import abc時(shí)候,這個(gè)時(shí)候如果我們執(zhí)行def.py,那么abc.py中if __name__ == "__main__":就不會是程序入口main了,如果我們不調(diào)用abc.py執(zhí)行的話,abc.py中的代碼是不會執(zhí)行的。這個(gè)時(shí)候abc.py中的__name__的值就是模塊aaa的名字了。
2.什么是multiprocessing?
? ? ? Unix/Linux操作系統(tǒng)提供了一個(gè)fork()系統(tǒng)調(diào)用,可以用來創(chuàng)建進(jìn)程。它非常特殊。普通的函數(shù)調(diào)用,調(diào)用一次,返回一次,但是fork()調(diào)用一次,返回兩次,因?yàn)椴僮飨到y(tǒng)自動把當(dāng)前進(jìn)程(稱為父進(jìn)程)復(fù)制了一份(稱為子進(jìn)程),然后,分別在父進(jìn)程和子進(jìn)程內(nèi)返回。
? ? 但是由于Windows沒有fork調(diào)用,所以為了支持跨平臺,pytho搞了個(gè)跨平臺multiprocessing實(shí)現(xiàn)多進(jìn)程,但是盡管如此在windows上和linux上,用multiprocessing實(shí)現(xiàn)方式還是不太一樣。在windows上會有一個(gè)import創(chuàng)建進(jìn)程的模塊的操作,而linux上就沒有,基于fork。在windows上,子進(jìn)程會自動import啟動它的這個(gè)文件,而在import的時(shí)候是會自動執(zhí)行這些語句的(意思說子進(jìn)程會復(fù)制并以import的形式導(dǎo)入執(zhí)行主進(jìn)程中代碼,如果你把要執(zhí)行的代碼放到了if__name__ == "__main__"中,那么這個(gè)時(shí)候因?yàn)槭莍mport,所以這個(gè)時(shí)候就不會執(zhí)行該代碼了)。所以創(chuàng)建進(jìn)程的操作要用if __name__ == "__main__":保護(hù)起來,否則就會遞歸創(chuàng)建進(jìn)程,或者出其它什么錯誤。