在Python中混合同步和A同步代码

问题描述 投票:1回答:1

我正在尝试将基于回调的Python代码中的同步流转换为使用asyncio的A同步流。基本上,代码与TCP / UNIX套接字交互很多。它从套接字读取数据,操纵它来做出决定并将东西写回另一方。这是在多个套接字上同时进行的,数据在上下文之间共享以便有时做出决策。

编辑::当前代码主要基于为特定套接字注册回调到中央实体,并且当相关套接字可读时让该实体运行回调(类似于“当该套接字具有要读取的数据时调用此函数” “)。调用回调后 - 会发生一堆事情,并最终在新数据可用时注册新的回调。中央实体对所有已注册的套接字运行选择,以确定应调用哪些回调。

我试图这样做而不重构我的整个代码并使程序员尽可能无缝 - 所以我试图这样思考 - 所有代码应该像今天一样运行 - 但无论何时当前代码执行socket.recv()以获取新数据 - 该进程将执行其他任务。当读取返回时,它应该返回使用它获得的新数据从同一点处理数据。

为此,我编写了一个名为AsyncSocket的新类 - 它与asyncIO的IO流交互并将Async / await语句几乎只放在那里 - 认为我会在我的类中实现recv方法,使其看起来像“常规IO套接字“到我的其余代码。到目前为止 - 这是我对A-sync编程应该允许的理解。

现在来问题:

我的代码等待客户端连接 - 当它执行时,允许每个客户端的上下文从它自己的连接读取和写入。我已经简化到以下内容以澄清问题:

class AsyncSocket():
    def __init__(self,reader,writer):
        self.reader = reader
        self.writer = writer
    def recv(self,numBytes):
        print("called recv!")
        data = self.read_mitigator(numBytes)
        return data
    async def read_mitigator(self,numBytes):
        print("Awaiting of AsyncSocket.reader.read")
        data = await self.reader.read(numBytes)
        print("Done Awaiting of AsyncSocket.reader.read data is %s " % data)
        return data 

def mit2(aSock):
    return mit3(aSock)

def mit3(aSock):
    return aSock.recv(100)

async def echo_server(reader, writer):
    print ("New Connection!")
    aSock = AsyncSocket(reader,writer) # create a new A-sync socket class and pass it on the to regular code

    while True:
        data = await some_func(aSock) # this would eventually read from the socket
        print ("Data read is %s" % (data))
        if not data:
            break
        writer.write(data) # echo everything back

async def main(host, port):
    server = await asyncio.start_server(echo_server, host, port)
    await server.serve_forever()
asyncio.run(main('127.0.0.1', 5000))

mit2()和mit3()是同步函数,它们在返回主客户端循环之前回过头来处理数据 - 但是在这里我只是将它们用作空函数。当我使用some_func()的实现时,问题就开始了。

传递实现(编辑:工作类型) - 但仍有问题:

def some_func(aSock):
    try:
        return (mit2(aSock)) # works
    except:
        print("Error!!!!")

虽然一个实现读取数据并对其执行某些操作 - 比如在返回之前添加后缀,但会抛出错误:

def some_func(aSock):
    try:
        return (mit2(aSock) + "something") # doesn't work
    except:
        print("Error!!!!")

错误(据我所知)意味着它并没有真正做到它应该做的事情:

New Connection!
called recv!
/Users/user/scripts/asyncServer.py:36: RuntimeWarning: coroutine 'AsyncSocket.read_mitigator' was never awaited
  return (mit2(aSock) + "something") # doesn't work
RuntimeWarning: Enable tracemalloc to get the object allocation traceback
Error!!!!
Data read is None

并且echo服务器显然不起作用。很显然,我的代码看起来更像是#2选项,在some_func(),mit2()和mit3()中有更多的东西 - 但是我无法让它工作。我在使用asyncio / async / await时相当新 - 所以我错过了什么(相当基本的概念)?

python python-3.x async-await python-asyncio
1个回答
3
投票

此代码无法按预期工作:

def recv(self,numBytes):
    print("called recv!")
    data = self.read_mitigator(numBytes)
    return data

async def read_mitigator(self,numBytes):
    ...

您不能从同步函数调用异步函数并获得结果,您必须等待它,这可确保您在数据尚未就绪时返回事件循环。异步和同步代码之间的这种不匹配有时被称为function color的问题。

由于您的代码已经使用了非阻塞套接字和事件循环,因此将其移植到asyncio的好方法可能是首先切换到asyncio事件循环。您可以使用sock_recv等事件循环方法来请求数据:

def start():
    loop = asyncio.get_event_loop()
    sock = make_socket()  # make sure it's non-blocking
    future_data = loop.sock_recv(sock, 1024)
    future_data.add_done_callback(continue_read)
    # return to the event loop - when some data is ready
    # continue_read will be invoked

def continue_read(future):
    data = future.result()
    print('got', data)
    # ... do something with data, e.g. process it
    # and call sock_sendall with the response

asyncio.get_event_loop().call_soon(start())
asyncio.get_event_loop().run_forever()

一旦你让程序在该模式下工作,你就可以开始转向协程,这允许代码看起来像同步代码,但工作方式完全相同:

async def start():
    loop = asyncio.get_event_loop()
    sock = make_socket()  # make sure it's non-blocking
    data = await loop.sock_recv(sock, 1024)
    # data is available "immediately", meaning the coroutine gets
    # automatically suspended when awaiting data that is not yet
    # ready, and automatically re-scheduled when the data is ready
    print('got', data)

asyncio.run(start())

下一步可以消除make_socket并切换到asyncio streams

© www.soinside.com 2019 - 2024. All rights reserved.