摘要:后來通過調(diào)查發(fā)現(xiàn)是因?yàn)槌绦蛑型瑫r(shí)使用了多線程,多進(jìn)程以及模塊,導(dǎo)致子進(jìn)程中出現(xiàn)了死鎖的情況。當(dāng)創(chuàng)建子進(jìn)程的時(shí)候,后臺(tái)線程中的模塊正好獲取了一個(gè)鎖在記錄日志信息。
前段時(shí)間有個(gè)程序突然出現(xiàn)了子進(jìn)程不工作的情況。
后來通過調(diào)查發(fā)現(xiàn)是因?yàn)槌绦蛑型瑫r(shí)使用了多線程,多進(jìn)程以及 logging 模塊,導(dǎo)致子進(jìn)程中出現(xiàn)了死鎖的情況。
當(dāng)創(chuàng)建子進(jìn)程的時(shí)候,后臺(tái)線程中的 logging 模塊正好獲取了一個(gè)鎖(threading.RLock)在記錄日志信息。由于在 unix/linux 平臺(tái)下 Python 是通過 fork 來創(chuàng)建子進(jìn)程的,因此創(chuàng)建子進(jìn)程的時(shí)候會(huì)把 logging 中的鎖也復(fù)制了一份,當(dāng)子進(jìn)程中需要記錄日志的時(shí)候發(fā)現(xiàn) logging 的鎖一直處于被占用的狀態(tài),從而出現(xiàn)了死鎖(復(fù)制的這個(gè)鎖永遠(yuǎn)也不會(huì)被釋放,因?yàn)樗乃姓呤歉高M(jìn)程的某個(gè)線程,但是這個(gè)線程釋放鎖的時(shí)候又不會(huì)影響子進(jìn)程里的這個(gè)鎖)。
復(fù)現(xiàn)問題的代碼如下:
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 當(dāng)前處于父進(jìn)程 print("parent: running (rlock = {0})".format(global_rlock)) else: # pid = 0 當(dāng)前處于子進(jìn)程 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)
上面代碼的執(zhí)行結(jié)果如下:
$ 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
從上面的結(jié)果中可以看出來:雖然線程隨后釋放了獲得的鎖,但是子進(jìn)程卻永遠(yuǎn)的卡在了獲取鎖的地方。
那么, 應(yīng)該如何解決這個(gè)問題呢?至少有三種解決辦法:
先創(chuàng)建子進(jìn)程,然后再創(chuàng)建線程:
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 當(dāng)前處于父進(jìn)程 print("parent: running (rlock = {0})".format(global_rlock)) worker.start() else: # pid = 0 當(dāng)前處于子進(jìn)程 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)
結(jié)果:
$ 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
可以看到子進(jìn)程和線程都能夠正常獲取鎖。
不要混合使用 threading, multiprocessing, logging/其他使用了線程鎖的模塊。 要么都是多線程,要么都是多進(jìn)程。
另一個(gè)辦法就是配置 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調(diào)用start方法后,有較小的幾率子進(jìn)程中run方法未執(zhí)行 - 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...
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/44210.html
摘要:說一下進(jìn)程線程以及多任務(wù)多進(jìn)程多線程和協(xié)程進(jìn)程概念一個(gè)程序?qū)?yīng)一個(gè)進(jìn)程,這個(gè)進(jìn)程被叫做主進(jìn)程,而一個(gè)主進(jìn)程下面還有許多子進(jìn)程。避免了由于系統(tǒng)在處理多進(jìn)程或者多線程時(shí),切換任務(wù)時(shí)需要的等待時(shí)間。 showImg(https://segmentfault.com/img/bVbuYxg?w=3484&h=2480); 閱讀本文大約需要 10 分鐘。 14.說一下進(jìn)程、線程、以及多任務(wù)(多進(jìn)...
摘要:主進(jìn)程會(huì)等待所有的子進(jìn)程先結(jié)束,然后再結(jié)束主進(jìn)程。關(guān)閉進(jìn)程池,關(guān)閉后實(shí)例不再接收新的請(qǐng)求等待實(shí)例中的所有子進(jìn)程執(zhí)行完畢,主進(jìn)程才會(huì)退出,必須放在語句之后。主進(jìn)程一般都用來等待,任務(wù)在子進(jìn)程中執(zhí)行。 多任務(wù):同一個(gè)時(shí)間段中,執(zhí)行多個(gè)函數(shù)/運(yùn)行多個(gè)程序. 操作系統(tǒng)可以同時(shí)運(yùn)行多個(gè)任務(wù):操作系統(tǒng)輪流讓各個(gè)任務(wù)交替執(zhí)行,任務(wù)1執(zhí)行0.01秒,切換到任務(wù)2,任務(wù)2執(zhí)行0.01秒,再切換到任務(wù)3,...
摘要:多線程的理解多進(jìn)程和多線程都可以執(zhí)行多個(gè)任務(wù),線程是進(jìn)程的一部分。多線程創(chuàng)建在中,同樣可以實(shí)現(xiàn)多線程,有兩個(gè)標(biāo)準(zhǔn)模塊和,不過我們主要使用更高級(jí)的模塊。多線程的應(yīng)用場(chǎng)景。 1、多線程的理解 多進(jìn)程和多線程都可以執(zhí)行多個(gè)任務(wù),線程是進(jìn)程的一部分。線程的特點(diǎn)是線程之間可以共享內(nèi)存和變量,資源消耗少(不過在Unix環(huán)境中,多進(jìn)程和多線程資源調(diào)度消耗差距不明顯,Unix調(diào)度較快),缺點(diǎn)是線程之間...
摘要:某進(jìn)程內(nèi)的線程在其它進(jìn)程不可見。線程的實(shí)體包括程序數(shù)據(jù)和。包括以下信息線程狀態(tài)。當(dāng)線程不運(yùn)行時(shí),被保存的現(xiàn)場(chǎng)資源。用戶級(jí)線程執(zhí)行系統(tǒng)調(diào)用指令時(shí)將導(dǎo)致其所屬進(jìn)程被中斷,而內(nèi)核支持線程執(zhí)行系統(tǒng)調(diào)用指令時(shí),只導(dǎo)致該線程被中斷。線程能夠利用的表空 操作系統(tǒng)線程理論 線程概念的引入背景 進(jìn)程之前我們已經(jīng)了解了操作系統(tǒng)中進(jìn)程的概念,程序并不能單獨(dú)運(yùn)行,只有將程序裝載到內(nèi)存中,系統(tǒng)為它分配資源才能運(yùn)...
摘要:中關(guān)于線程的標(biāo)準(zhǔn)庫是,之前在版本中的在之后更名為,無論是還是都應(yīng)該盡量避免使用較為底層的而應(yīng)該使用。而與線程相比,協(xié)程尤其是結(jié)合事件循環(huán)無論在編程模型還是語法上,看起來都是非常友好的單線程同步過程。 項(xiàng)目地址:https://git.io/pytips 要說到線程(Thread)與協(xié)程(Coroutine)似乎總是需要從并行(Parallelism)與并發(fā)(Concurrency)談起...
閱讀 1893·2023-04-25 14:33
閱讀 3475·2021-11-22 15:22
閱讀 2562·2021-09-30 09:48
閱讀 2877·2021-09-14 18:01
閱讀 1822·2019-08-30 15:55
閱讀 3126·2019-08-30 15:53
閱讀 2232·2019-08-30 15:44
閱讀 732·2019-08-30 10:58