摘要:后來通過調查發現是因為程序中同時使用了多線程,多進程以及模塊,導致子進程中出現了死鎖的情況。當創建子進程的時候,后臺線程中的模塊正好獲取了一個鎖在記錄日志信息。
前段時間有個程序突然出現了子進程不工作的情況。
后來通過調查發現是因為程序中同時使用了多線程,多進程以及 logging 模塊,導致子進程中出現了死鎖的情況。
當創建子進程的時候,后臺線程中的 logging 模塊正好獲取了一個鎖(threading.RLock)在記錄日志信息。由于在 unix/linux 平臺下 Python 是通過 fork 來創建子進程的,因此創建子進程的時候會把 logging 中的鎖也復制了一份,當子進程中需要記錄日志的時候發現 logging 的鎖一直處于被占用的狀態,從而出現了死鎖(復制的這個鎖永遠也不會被釋放,因為它的所有者是父進程的某個線程,但是這個線程釋放鎖的時候又不會影響子進程里的這個鎖)。
復現問題的代碼如下:
import os import sys import threading import time class ThreadWorker(threading.Thread): def __init__(self): print("ThreadWorker: init") super().__init__() def run(self): print("ThreadWorker: running (rlock = {0})".format(global_rlock)) global_rlock.acquire() print("ThreadWorker: i got lock {0}".format(global_rlock)) time.sleep(5) global_rlock.release() print("ThreadWorker: release lock {0} and " "sleeping forever".format(global_rlock)) time.sleep(600000) global_rlock = threading.RLock(verbose=True) worker = ThreadWorker() worker.start() time.sleep(1) print("forking") pid = os.fork() if pid != 0: # pid != 0 當前處于父進程 print("parent: running (rlock = {0})".format(global_rlock)) else: # pid = 0 當前處于子進程 print("child: running (rlock = {0}), " "getting the lock...".format(global_rlock)) global_rlock.acquire() print("child: got the lock {0}".format(global_rlock)) sys.exit(0) time.sleep(10)
上面代碼的執行結果如下:
$ python fork.py ThreadWorker: init ThreadWorker: running (rlock =) ThreadWorker: i got lock forking parent: running (rlock = ) child: running (rlock = ), getting the lock... ThreadWorker: release lock and sleeping forever
從上面的結果中可以看出來:雖然線程隨后釋放了獲得的鎖,但是子進程卻永遠的卡在了獲取鎖的地方。
那么, 應該如何解決這個問題呢?至少有三種解決辦法:
先創建子進程,然后再創建線程:
import os import sys import threading import time class ThreadWorker(threading.Thread): def __init__(self): print("ThreadWorker: init") super().__init__() def run(self): print("ThreadWorker: running (rlock = {0})".format(global_rlock)) global_rlock.acquire() print("ThreadWorker: i got lock {0}".format(global_rlock)) time.sleep(5) global_rlock.release() print("ThreadWorker: release lock {0} and " "sleeping forever".format(global_rlock)) time.sleep(600000) global_rlock = threading.RLock(verbose=True) worker = ThreadWorker() print("forking") pid = os.fork() if pid != 0: # pid != 0 當前處于父進程 print("parent: running (rlock = {0})".format(global_rlock)) worker.start() else: # pid = 0 當前處于子進程 time.sleep(1) print("child: running (rlock = {0}), " "getting the lock...".format(global_rlock)) global_rlock.acquire() print("child: got the lock {0}".format(global_rlock)) global_rlock.release() print("child: release the lock {0}".format(global_rlock)) sys.exit(0) time.sleep(10)
結果:
$ python fork2.py ThreadWorker: init forking parent: running (rlock =) ThreadWorker: running (rlock = ) ThreadWorker: i got lock child: running (rlock = ), getting the lock... child: got the lock child: release the lock ThreadWorker: release lock and sleeping forever
可以看到子進程和線程都能夠正常獲取鎖。
不要混合使用 threading, multiprocessing, logging/其他使用了線程鎖的模塊。 要么都是多線程,要么都是多進程。
另一個辦法就是配置 logging 使用無鎖的 handler 來記錄日志信息。
參考資料PythonLoggingThreadingMultiprocessingIntermixedStudy(Using modules Python logging, threading and multiprocessing in a single application.)
Issue 6721: Locks in the standard library should be sanitized on fork - Python tracker
multithreading - Deadlock with logging multiprocess/multithread python script - Stack Overflow
python - 使用multiprocessing.Process調用start方法后,有較小的幾率子進程中run方法未執行 - SegmentFault
python multiprocessing hanging, potential queue memory error? - Stack Overflow
Threads and fork(): think twice before mixing them. | Linux Programming Blog
原文地址: https://mozillazg.com/2016/09...
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/44210.html
摘要:說一下進程線程以及多任務多進程多線程和協程進程概念一個程序對應一個進程,這個進程被叫做主進程,而一個主進程下面還有許多子進程。避免了由于系統在處理多進程或者多線程時,切換任務時需要的等待時間。 showImg(https://segmentfault.com/img/bVbuYxg?w=3484&h=2480); 閱讀本文大約需要 10 分鐘。 14.說一下進程、線程、以及多任務(多進...
摘要:主進程會等待所有的子進程先結束,然后再結束主進程。關閉進程池,關閉后實例不再接收新的請求等待實例中的所有子進程執行完畢,主進程才會退出,必須放在語句之后。主進程一般都用來等待,任務在子進程中執行。 多任務:同一個時間段中,執行多個函數/運行多個程序. 操作系統可以同時運行多個任務:操作系統輪流讓各個任務交替執行,任務1執行0.01秒,切換到任務2,任務2執行0.01秒,再切換到任務3,...
摘要:多線程的理解多進程和多線程都可以執行多個任務,線程是進程的一部分。多線程創建在中,同樣可以實現多線程,有兩個標準模塊和,不過我們主要使用更高級的模塊。多線程的應用場景。 1、多線程的理解 多進程和多線程都可以執行多個任務,線程是進程的一部分。線程的特點是線程之間可以共享內存和變量,資源消耗少(不過在Unix環境中,多進程和多線程資源調度消耗差距不明顯,Unix調度較快),缺點是線程之間...
摘要:某進程內的線程在其它進程不可見。線程的實體包括程序數據和。包括以下信息線程狀態。當線程不運行時,被保存的現場資源。用戶級線程執行系統調用指令時將導致其所屬進程被中斷,而內核支持線程執行系統調用指令時,只導致該線程被中斷。線程能夠利用的表空 操作系統線程理論 線程概念的引入背景 進程之前我們已經了解了操作系統中進程的概念,程序并不能單獨運行,只有將程序裝載到內存中,系統為它分配資源才能運...
摘要:中關于線程的標準庫是,之前在版本中的在之后更名為,無論是還是都應該盡量避免使用較為底層的而應該使用。而與線程相比,協程尤其是結合事件循環無論在編程模型還是語法上,看起來都是非常友好的單線程同步過程。 項目地址:https://git.io/pytips 要說到線程(Thread)與協程(Coroutine)似乎總是需要從并行(Parallelism)與并發(Concurrency)談起...
閱讀 1778·2023-04-25 14:33
閱讀 3378·2021-11-22 15:22
閱讀 2177·2021-09-30 09:48
閱讀 2684·2021-09-14 18:01
閱讀 1740·2019-08-30 15:55
閱讀 3006·2019-08-30 15:53
閱讀 2139·2019-08-30 15:44
閱讀 648·2019-08-30 10:58