Python中if __name__=="__main__" 語句在調用多進程Process過程中的作用分析

来源:https://www.cnblogs.com/wFrancow/archive/2018/03/05/8511711.html
-Advertisement-
Play Games

if __name__=="__main__" 語句在調用多進程Process過程中是必須的,否則會報如下錯誤An attempt has been made to start a new process before the current process has finished its boo... ...


2018年2月27日 於創B515

  

引言
  最近準備學習一下如何使用Python中的多進程。在翻看相關書籍、網上資料時發現所有代碼都含有if __name__=="__main__",在實驗的過程中發現如果在運行代碼過程中,沒有這句話Python解釋器就會報錯。雖然Python對於multiprocessing的文檔第17.2.1.1節中【1】提到必須如此使用,但是我覺得並沒有根本上解釋清楚。因此我決定從源碼來解釋我的疑惑。

# 代碼0.1錯誤代碼
import
multiprocessing as mp import os def do(): print("pid is : %s ..." % os.getpid()) print("parent id is : %s ..." % os.getpid()) p = mp.Process(target=do, args=()) p.start()
# 代碼0.2正確代碼
import multiprocessing as mp
import os

def do():
    print("pid is : %s ..." % os.getpid())

if __name__ == '__main__':
    print("parent id is : %s ..." % os.getpid())
    p = mp.Process(target=do, args=())
    p.start()

 

問題描述
問題
在運行代碼-0.1時,會出現RuntimeError,錯誤提示如下。但是運行代碼0.2時就不會,一切順利。

    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.

 

問題產生的環境

環境配置
運行環境:  Win10
IDE Sublime Text3

 

簡單解釋
  由於Python運行過程中,新創建進程後,進程會導入正在運行的文件,即在運行代碼0.1的時候,代碼在運行到mp.Process時,新的進程會重新讀入改代碼,對於沒有if __name__=="__main__"保護的代碼,新進程都認為是要再次運行的代碼,這是子進程又一次運行mp.Process,但是在multiprocessing.Process的源碼中是對子進程再次產生子進程是做了限制的,是不允許的,於是出現如上的錯誤提示。

詳細解釋
先談一談if__name__=="__main__"
  在Python有關__main__的文檔中【2】說明“__main__”是代碼執行時的最高的命名空間(the name of the scope in which top-level code executes),當代碼被當做腳本讀入的時候,命名空間會被命名為“__main__”,對於在腳本運行過程中讀入的代碼命名空間都不會被命名為“__main__”。這也就是說創建的子進程是不會讀取__name__=="__main__"保護下的代碼。

再談一談multiprocessing(win32下的源碼分析)
multiprocessing根據平臺不同會執行不同的代碼:在類UNIX系統下由於操作系統本身支持fork()語句,win32系統由於本身不支持fork(),因此在兩種系統下multiprocessing會運行不同的代碼,如圖1 UNIX平臺、圖2 win32平臺(包含在context.py文件中,Process的定義也是在context.py文件中)。

圖1 對於類UNIX系統平臺

圖2 對於win32系統平臺

  Process是一個高度依賴繼承的類———其父類是BaseProcess,如圖3 Process類的定義。在使用過程中先初始化一個Process實例,然後通過Process.start()來啟動子進程。我們繼續看關於Process.start()的定義,如圖4 BaseProcess類中的start()定義。在其中第105行,調用了self._Popen(self),該函數重定義定義於Process類。該函數按調用順序最後會跳轉到 popen_spwan_win32.py 文件中的Popen類,如圖5 Popen類的定義。Popen類在初始話過程中首先調用windows相關介面,生成一個管道(38行),取得對管道讀寫的句柄,然後生成一個命令行的字元串列表——cmd,如下:

['C:\\Program Files\\Python35\\python.exe', '-B', '-c', 
'from multiprocessing.spawn import spawn_main;spawn_main(pipe_handle=928, parent_pid=9292)', 
'--multiprocessing-fork']

圖3 Process類的定義

圖4 BaseProcess類中的start()定義

圖5 Popen類的定義

  這個字元串列表之後通過命令行送入系統,得到相應的子進程和子線程的句柄及子進程和子線程的ID號。在第65、66行是通過pickle方法將必要的數據從父進程傳輸給子進程。新進程是調用spawn.py文件中的spawn_main函數,如圖6 spawnmain的定義。其中第100行的steal_handle()的作用是子進程獲取父進程生成的句柄,用於後續通信——使用pickle方法從父進程中讀取必要數據。而問題的出現就是在這之後出現!該代碼在第106行調用_main(),其次在第115行調用prepare(),再次運行到223行時,運行_fixup_main_from_name(),而此時該函數會運行父進程的腳本。因此對於沒有if__name__=="__main__"保護的代碼都是要運行的,而此時在第二次運行Process創建新進程的時候在第123行 if getattr(process.current_process(), '_inheriting', False): 時,由於子進程是具有_inheriting屬性,因此會激發出上述錯誤代碼。

圖6 spawn_main的定義

  對於代碼中生成新子進程時所用到的兩個技術我覺很有趣也很苦惱,因此決定繼續看下去。下麵簡單描述一下兩個技術的概況。

 —— pickle模塊

  首先是pickle模塊。pickle模塊是一個Python中特有的數據格式,與JSON等不同,是不能被其他語言識別的格式。在Python中的官方文檔【3】中比較了pickle模塊與JSON的不同之處,以及介紹了pickle的使用條件。簡單摘錄如下:

           pickle與JSON的區別             
  • JSON 是text文本格式而pickle是二進位流
  • JSON 是可讀的而pickle是不可讀的
  • JSON 可用於其他語言環境,而pickle僅僅用於Python自身
  • JSON 對於Python中的一些內建(built-in)類型會失效,而pickle都是有效的
可以被pickle模塊pickle的數據
  • None、True、False
  • 整數、浮點數、複數
  • 字元串、位元組、位元組數組
  • 包含可以被pickle數據的元組、列表、集合、字典
  • 使用def定義的函數
  • 在模塊top-level定義的內建(built-in)函數和類

 

 —— 管道(Pipe)
  這裡理解管道是從類UNIX系統理解,因為其理解起來更方便。管道在類UNIX系統中也是一種文件,在生成新的子進程的時候將兩個進程都關聯至同一個管道上,這樣就是雙工通信(父子進程相互可讀可寫)。如果要實現單工通信,就關閉相應的通道(一方寫一方讀)【4】

參考文獻

  1. Python3.5 關於multiprocessing的文檔  https://docs.python.org/3.5/library/multiprocessing.html
  2. Python3.5 關於\_\_main\_\_的文檔     https://docs.python.org/3.5/library/__main__.html
  3. Python3.5 關於pickle的文檔                       https://docs.python.org/3.5/library/pickle.html?highlight=pickle#module-pickle
  4. 網上一片關於pipe的介紹                             http://www.cnblogs.com/qiaoyanlin/p/7576085.html

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 通過統計資料庫中的1000多個項目,我們發現在 JavaScript 中最常出現的錯誤有10個。本文會向大家介紹這些錯誤發生的原因以及如何防止。 ...
  • 近來區塊鏈一詞很熱門,網路上關乎其討論也很多,這裡就不解釋了,畢竟幾句話也是說不清楚的。 最近得空利用HTML5+css3+jQ開發了一個移動端的區塊鏈項目,感覺界面、佈局、效果還是ok的。 項目效果截圖: 對於區塊鏈,這裡有詳細解釋: https://www.zhihu.com/question/ ...
  • git進行管理時,.gitignore是必不可少的,可以指定不需要提交到倉庫的資源。最好在git init之後就創建 .gitignore文件,這是個好習慣,常用的配置及說明如下: ...
  • Spring security記住我基本原理: 登錄的時候,請求發送給過濾器UsernamePasswordAuthenticationFilter,當該過濾器認證成功後,會調用RememberMeService,會生成一個token,將token寫入到瀏覽器cookie,同時RememberMeS ...
  • 前言: 關於為什麼要引入dubbo框架,而不是用spring cloud或者是motan呢,主要是筆者現在公司用的就是dubbo,並且第一次接觸到微服務的概念是來源於dubbo,再加上最近dubbo頻繁的更新,所以就有採用dubbo改造的想法。建議沒看過這個教程的園友可以先看看原來的教程,因為現在所 ...
  • 時常出差,經常填寫發票,有時網路不好,數數也不會了 於是,一個隨身小應用就這麼誕生 沒錯,我只為了一時的方便~ 有一個數字:1314521.94 我想轉換為大寫 但是它有點長,還有個小數點 於是,我掏出手機 截了一張圖 突然很多人都會了 PS 告訴我你的數字 如果你想將100轉為大寫,只需要發送 金 ...
  • 1、逆向工程的作用 Mybatis 官方提供了逆向工程,可以針對資料庫表自動生成Mybatis執行所需要的代碼(包括mapper.xml、Mapper.java、pojo)。 2、逆向工程的使用方法 逆向工程需要的jar包如下圖所示: ![][1] 也可以直接下載我Github上面的源代碼( "ht ...
  • 前言:有時候無聊看一些搞笑的段子,糗事百科還是個不錯的網站,所以就想用Python來玩一下。也比較簡單,就寫出來分享一下。嘿嘿 環境:Python 2.7 + win7 現在開始,打開糗事百科網站,先來分析。地址:https://www.qiushibaike.com 一般像這種都是文本的話,查看源 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...