Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/assembly/6.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 如何手动退出无限trio循环,如trio';s教程回送客户端_Python_Python Trio - Fatal编程技术网

Python 如何手动退出无限trio循环,如trio';s教程回送客户端

Python 如何手动退出无限trio循环,如trio';s教程回送客户端,python,python-trio,Python,Python Trio,除了使用Ctrl-C或使用超时之外,是否有其他方法可以手动退出trio无限循环(如trio教程中的echo客户端) 我的想法是使用从另一个python脚本调用echo客户端,并能够使用相同的python脚本任意关闭它。我正在考虑使用一个标志(可能是事件?)作为开关来触发托儿所中的cancel\u scope.cancel()。但我不知道如何触发开关。下面是我修改教程echo客户端代码的尝试 import sys import trio PORT = 12345 BUFSIZE = 16384

除了使用
Ctrl-C
或使用超时之外,是否有其他方法可以手动退出trio无限循环(如trio教程中的echo客户端)

我的想法是使用从另一个python脚本调用echo客户端,并能够使用相同的python脚本任意关闭它。我正在考虑使用一个标志(可能是事件?)作为开关来触发托儿所中的
cancel\u scope.cancel()
。但我不知道如何触发开关。下面是我修改教程echo客户端代码的尝试

import sys
import trio

PORT = 12345
BUFSIZE = 16384
FLAG = 1 # FLAG is a global variable

async def sender(client_stream):
    print("sender: started")
    while FLAG:
        data = b'async can sometimes be confusing but I believe in you!'
        print(f"sender: sending {data}")
        await client_stream.send_all(data)
        await trio.sleep(1)

async def receiver(client_stream):
    print("recevier: started!")
    while FLAG:
        data = await client_stream.receive_some(BUFSIZE)
        print(f"receiver: got data {data}")
        if not data:
            print("receiver: connection closed")
            sys.exit()

async def checkflag(nursery): # function to trigger cancel()
    global FLAG
    if not FLAG:
        nursery.cancel_scope.cancel()
    else:
        # keep this task running if not triggered, but how to trigger it, 
        # without Ctrl-C or timeout?
        await trio.sleep(1) 

async def parent():
    print(f"parent: connecting to 127.0.0.1:{PORT}")
    client_stream = await trio.open_tcp_stream("127.0.0.1", PORT)
    async with client_stream:
        async with trio.open_nursery() as nursery:
            print("parent: spawning sender ...")
            nursery.start_soon(sender, client_stream)

            print("parent: spawning receiver ...")
            nursery.start_soon(receiver, client_stream)

            print("parent: spawning checkflag...")
            nursery.start_soon(checkflag, nursery)

        print('Close nursery...')
    print("Close stream...")

trio.run(parent)

我发现我无法在
trio.run()
之后将任何命令输入到python REPL中,以手动更改
标志
,我想知道是否从另一个脚本调用此echo客户端,如何准确地触发托儿所中的
cancel\u scope.cancel()
?还是有更好的办法?非常感谢大家的帮助。谢谢。

有很多方法可以做到这一点。为什么不直接使用
Ctrl-C
?对我来说似乎完全正确

如果你真的不想使用
Ctrl-C
,那么你需要一个函数来监听输入和更新
标志(或者直接退出程序;老实说,我认为你根本不需要
标志
逻辑)


例如,您可以有一个函数,该函数可以从文件轮询/从db读取/侦听终端输入等,并并行运行。侦听器应作为同一Python脚本中的独立工作程序运行。但是,根据您选择的方式,更改外部输入的函数可以是独立的脚本

如果您想使用键盘输入退出,这里有一个针对Linux和Mac OS X的解决方案。您可以使用Python模块在Windows上执行类似的操作

我从中复制了
echo client.py
,并在添加的三个代码块上添加了一条“新”注释。在REPL中,您可以键入“q”取消托儿所作用域并退出:

# -- NEW
import termios, tty

import sys
import trio

PORT = 12345
BUFSIZE = 16384

# -- NEW
async def keyboard():
    """Return an iterator of characters from stdin."""
    stashed_term = termios.tcgetattr(sys.stdin)
    try:
        tty.setcbreak(sys.stdin, termios.TCSANOW)
        while True:
            yield await trio.run_sync_in_worker_thread(
                sys.stdin.read, 1,
                cancellable=True
            )
    finally:
        termios.tcsetattr(sys.stdin, termios.TCSANOW, stashed_term)

async def sender(client_stream):
    print("sender: started!")
    while True:
        data = b"async can sometimes be confusing, but I believe in you!"
        print("sender: sending {!r}".format(data))
        await client_stream.send_all(data)
        await trio.sleep(1)

async def receiver(client_stream):
    print("receiver: started!")
    while True:
        data = await client_stream.receive_some(BUFSIZE)
        print("receiver: got data {!r}".format(data))
        if not data:
            print("receiver: connection closed")
            sys.exit()

async def parent():
    print("parent: connecting to 127.0.0.1:{}".format(PORT))
    client_stream = await trio.open_tcp_stream("127.0.0.1", PORT)
    async with client_stream:
        async with trio.open_nursery() as nursery:
            print("parent: spawning sender...")
            nursery.start_soon(sender, client_stream)

            print("parent: spawning receiver...")
            nursery.start_soon(receiver, client_stream)

            # -- NEW
            async for key in keyboard():
                if key == 'q':
                    nursery.cancel_scope.cancel()

trio.run(parent)
调用
tty.setcbreak
会将终端置于无缓冲模式,因此在程序接收输入之前,您不必按return键。它还可以防止字符回显到屏幕上。此外,顾名思义,它允许
Ctrl-C
正常工作

finally
块中,
termios.tcsetattr
将终端恢复到
tty.setcbreak
之前的任何模式。因此,您的终端在退出时恢复正常

sys.stdin.read
在一个单独的线程中生成,因为它需要在阻塞模式下运行(在异步上下文中不好)。原因是
stdin
带有
stdout
stderr
。将
stdin
设置为非阻塞也会将
stdout
设置为非阻塞作为副作用,这可能会导致
print
功能出现问题(在我的情况下是截断)

进程间通信

下面是一个使用套接字从另一个Trio进程取消一个进程的基本示例:

# infinite_loop.py    
import trio    

async def task():     
    while True:       
        print("ping")    
        await trio.sleep(0.5)    

async def quitter(cancel_scope):      
    async def quit(server_stream):    
        await server_stream.receive_some(1024)    
        cancel_scope.cancel()    
    await trio.serve_tcp(quit, 12346)    

async def main():    
    async with trio.open_nursery() as nursery:    
        nursery.start_soon(task)    
        nursery.start_soon(quitter, nursery.cancel_scope)    

trio.run(main)

谢谢你的回复。想问一下,如何编写一个函数来监听终端输入?是否通过子流程模块?您不需要使用子流程。尝试使用
input()
命令。不过,我不能完全确定这在异步设置中是如何工作的。这里要注意的关键点是:一旦您在某个Trio任务中运行了一些代码,而该任务决定终止服务器,它就可以执行
turry.cancel\u scope.cancel()
。所有棘手的事情都在于判断是否按下了一个键。最终Trio将有一个更简单的方法来实现这一点,但控制台输入是超级复杂的,所以在它实现之前可能需要一点时间:感谢您的代码,它可以完美地工作。但是如果我想从另一个脚本(从另一个进程)触发取消,我是否也需要在这里生成一个单独的线程?如果有两个进程并行运行,则需要使用更通用的进程间通信(IPC)技术。Trio-偶数循环在单个线程中运行,最接近IPC的是,它允许直接的线程间通信。在本例中,我将在
托儿所
中生成另一个任务,该任务监听
套接字
中的q“quit”消息(类似于上面的
键盘
)。有时间我会修改我的答案。非常感谢!这正是我需要的!
# slayer.py        
import trio    

async def main():    
    async with await trio.open_tcp_stream("127.0.0.1", 12346) as s:
        await trio.sleep(3)    
        await s.send_all(b'quit')    

trio.run(main)