簡體   English   中英

為什么在不同線程中調用asyncio subprocess.communicate會掛起?

[英]Why does asyncio subprocess.communicate hang when called in different thread?

我有一種情況,當我必須在asyncio事件循環內運行子流程時,子流程通信會掛起,整個事情都在一個單獨的線程內。

我了解到,為了在單獨的線程中運行子進程,我需要

1. an event loop running in main thread, and
2. a child watcher must be initiated in main thread.

在滿足上述條件后,我完成了子流程工作。 但是subprocess.communicate現在正在掛起。 如果從主線程調用相同的代碼,則該代碼可以正常工作。

進一步挖掘之后,我觀察到通信正在掛起,因為該過程本身還沒有完成。 ie await process.wait()實際上掛起。

當我試圖在子進程中發出的命令本身掛起時,我已經看到了通信掛起,但是這里不是這種情況。

import asyncio
import shlex
import threading
import subprocess
async def sendcmd(cmd):
    cmdseq = tuple(shlex.split(cmd))
    print(cmd)
    p = await asyncio.create_subprocess_exec(*cmdseq, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    print(p.pid)
    output = (await asyncio.wait_for(p.communicate(), 5))[0]
    output = output.decode('utf8')
    print(output)
    return output


async def myfunc(cmd):
    o = await sendcmd(cmd)
    return o

def myfunc2():
    loop = asyncio.new_event_loop()
    asyncio.set_event_loop(loop)
    tasks = []
    tasks.append(asyncio.ensure_future(myfunc('uname -a')))
    loop.run_until_complete(asyncio.gather(*tasks))

async def myfunc3():
    t = threading.Thread(target=myfunc2)
    t.start()
    t.join()

def main():
    asyncio.get_child_watcher()
    loop = asyncio.get_event_loop()
    loop.run_until_complete(asyncio.ensure_future(myfunc3()))
    loop.close()

main()

子進程SIGCHLD似乎不是由工作線程而是由父線程接收的。 這意味着process.wait()不會由操作系統發出信號。 這里還有另一個討論

看起來兒童觀察者應該檢測到SIGCHLD並將其傳播到其他線程(或pid)及其事件循環,這似乎也是其主要設計目的。 (缺少文檔,因此需要閱讀源。)

注意:我認為t.join()阻塞了 運行兒童觀察程序 的主線程 ,因此需要進行修復。 我只是在其中放置一個while循環,並在t.is_alive()返回False時結束主事件循環。

我注意到signal_noop正在觸發,所以很好。 該問題似乎與似乎已正確設置的signal.set_wakeup_fd (self._csock.fileno())有關。 我需要進行更多調試,以了解該事件的處理方式以及主事件循環為何未收到該信號的原因。 我要指出的是, unix_events.py中的_process_self_data(self,data)並未發生。

信號和線程

即使在另一個線程中接收到信號,Python信號處理程序也總是在主Python線程中執行。 這意味着信號不能用作線程間通信的手段。 您可以改用來自線程模塊的同步原語。

此外,僅允許主線程設置新的信號處理程序。

我認為這可以解決。 對線程使用循環run_in_executor。

import asyncio
import shlex
import threading
import subprocess
import logging
async def sendcmd(cmd):
    cmdseq = tuple(shlex.split(cmd))
    print(cmd)
    p = await asyncio.create_subprocess_exec(*cmdseq, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    print(p.pid)
    output = (await asyncio.wait_for(p.communicate(), 5))[0]
    output = output.decode('utf8')
    print(output)
    return output

async def myfunc(cmd):
    o = await sendcmd(cmd)
    return o

def myfunc2():
    thread_loop = asyncio.new_event_loop()
    asyncio.set_event_loop(thread_loop)
    thread_loop.set_debug(True)     
    tasks = []
    tasks.append(asyncio.ensure_future(myfunc('uname -a')))
    thread_loop.run_until_complete(asyncio.gather(*tasks))
    thread_loop.close()

async def myfunc3(loop=None):
    await loop.run_in_executor(None, myfunc2)    

def main():
    logfilename='test.log'
    print('Writing log to {}'.format(logfilename))
    logging.basicConfig(filename=logfilename, level=logging.INFO, format='%(asctime)s %(name)s %(module)s %(levelname)-8s %(message)s')
    logging.getLogger('asyncio').setLevel(logging.DEBUG)
    root = logging.getLogger(__name__)

    cw=asyncio.get_child_watcher()
    main_loop = asyncio.get_event_loop()
    main_loop.run_until_complete(asyncio.ensure_future(myfunc3(loop=main_loop)))
    cw.close()
    main_loop.close()

main()

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM