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

2024-10-02 08:30:16 发布

您现在位置:Python中文网/ 问答频道 /正文

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

EDIT::当前的代码主要基于注册一个回调到一个特定套接字的中心实体,并在相关套接字可读时让该实体运行回调(类似于“当该套接字有数据要读取时调用此函数”)。一旦回调被调用,就会发生一系列的事情,最终在新数据可用时注册一个新的回调。中心实体对所有注册的套接字运行select,以确定应该调用哪些回调。在

我试图做到这一点,而不是重构我的整个代码,并使它尽可能无缝的程序员-所以我试着这样想-所有的代码都应该像今天一样运行-但是每当当前代码做接收插座()以获取新数据-该进程将执行其他任务。当read返回时,它应该返回到使用它获得的新数据处理来自同一点的数据。在

为此,我编写了一个名为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()是同步函数,它们在返回主客户机循环之前处理返回的数据,但这里我只是将它们用作空函数。 当我使用一些\u func()的实现时,问题就开始了。在

一个传递实现(编辑:有点管用),但仍然存在以下问题:

^{pr2}$

而读取数据并对其执行某些操作(如在返回前添加后缀)时,会抛出一个错误:

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

回音服务器显然不工作了。 显然,我的代码看起来更像是选项2,其中一些函数中有很多东西,mit2()和mit3(),但我不能让它工作。我在使用asyncio/async/await方面是一个相当新的概念,那么我遗漏了什么(我想是相当基本的概念)?在


Tags: 数据代码selfreaddatareturndefawait
1条回答
网友
1楼 · 发布于 2024-10-02 08:30:16

此代码无法按预期工作:

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

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

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

由于您的代码已经在使用非阻塞套接字和事件循环,所以将其移植到asyncio的一个好方法可能是首先切换到asyncio事件循环。您可以使用事件循环方法,如^{}请求数据:

^{pr2}$

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

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。在

相关问题 更多 >

    热门问题