簡體   English   中英

如何運行一個沒有結束而沒有等待結果的協程?

[英]How to run a coroutine that does not end without getting stuck awaiting a result?

我有這段代碼,我想連接到websocket並通過發送心跳保持連接活着。 在這樣做時,我也希望能夠將有效負載發送到websocket,但是我一直卡住等待keepAlive協程的結果

import asyncio
import websockets
ws=#path goes here
async def sendHeartbeat(socket):
    message={
        "op": 1,
        "d": 0
        }
    await socket.send(json.dumps(message))
    print("heartbeat sent")


async def keepAlive(socket):
    while 1==1:
        await sendHeartbeat(socket)
        resp=await(socket.recv())
        print(resp)
        if json.loads(resp)["op"]==11:
            #the server sent an ack back
            print("ack received")
        else:
            socket.close()
        await asyncio.sleep(10)




async def beginSocket(loop):
    async with websockets.connect(ws) as socket:
        print(await socket.recv())
        await keepAlive(socket)
        #payloads would be sent here
        print("Hey payloads being sent and stuff")

loop = asyncio.get_event_loop()
loop.run_until_complete(beginSocket(loop))

但是,使用這段代碼,等待keepAlive之后的print語句永遠不會打印出來。 我怎樣才能使代碼不等待keepAlive的結果?

雖然keepAlive(socket)會立即返回,但由於keepAlive是一個couroutine,因此await keepAlive(socket)永遠不會返回,因為keepAlive()包含一個無限循環。

而不是使用await ,嘗試asyncio.ensure_future(keepAlive(socket))

如果您確實想使用await keepAlive(socket) ,請嘗試從其他地方發送有效負載(可能事先使用asyncio.ensure_future(send_payload(socket)) )。

在這種情況下,您在概念上通過同一套接字進行兩次單獨的對話。 一個對話就是你的心跳和回復信息。 另一個是您要發送的其他數據包。

我會保留3個獨立的頂級(即他們直接向事件循環報告)任務。 我希望他們都能引用一些協調員,所以當有時間時,他們中的任何一個都可以取消所有其他協調員。

  1. 心跳任務,基本上是你的keepAlive功能。
  2. 處理你在websocket上進行的其他對話的任務。
  3. 多路復用從websocket讀取的任務。

多路復用任務的工作是將消息路由到適當的任務。 心跳任務應該只獲得心跳響應,而另一個任務應該獲取所有其他消息。

由於websockets已經對消息進行了框架處理,因此您只能sendrecv整個消息,因此它可能具有的其他作業無關緊要。

這是你可以寫這個的一種方式。

import asyncio
import websockets
ws=#path goes here

class RoutingTask(object):
    def __init__(self, sock, defaultQueue, **kwargs):
        super().__init__(**kwargs)
        self.sock = sock
        self.defaultQueue = defaultQueue # The queue all messages not otherwise matched go to.
        self.matchers = []

    async def run(self):
        while True:
            msg = await self.sock.recv()
            msg = json.loads(msg)
            matched = False
            for matcher, queue in matchers:
                if matcher(msg):
                    await queue.put(msg)
                    matched = True
                    break
            if not matched:
                await self.defaultQueue.put(msg)

    def addMatcher(self, matcher, queue):
        el = (matcher, queue)
        self.matchers.append(el)

async def heartbeatTask(wssock, incomingq):
    message=json.dumps({
        "op": 1,
        "d": 0
        }) # Do this just once.
    while True:
       await wssock.send(message)
       print("heartbeat sent")
       response = await asyncio.wait_for(incomingq.get(), 10) # Wait 10 seconds for response.
       assert response['op'] == 11
       print("heartbeat response received.")
       await asyncio.sleep(10) # Wait 10 seconds to send another heartbeat.

async def beginSocket(loop):
    def heartbeatMatcher(jsondict):
        return jsondict.get('op', None) == 11

    async with websockets.connect(ws) as socket:
        myq = asyncio.Queue(maxsize=1)
        heartbeatq = asyncio.Queue(maxsize=1)
        router = RoutingTask(socket, myq)
        router.addMatcher(heartbeatMatcher, heartbeatq)
        router = asyncio.ensure_future(router.run())
        heartbeat = asyncio.ensure_future(heartbeatTask(socket, heartbeatq)

        print(await myq.get())
        #payloads would be sent here
        print("Hey payloads being sent and stuff")

    heartbeat.cancel() # Stop the heartbeat
    router.cancel() # Stop the router task

loop = asyncio.get_event_loop()
loop.run_until_complete(beginSocket(loop))

這里有一些問題。 如果拋出異常, heartbeatrouter任務可能不會被取消。 他們也沒有很好的方法將問題報告回主beginSocket任務。 這基本上是一種快速和骯臟的一次性,以演示如何做你想做的事情。

在我看來, asyncio.ensure_future被錯誤命名。 它的作用是告訴事件循環它需要保持運行的新事物。 它基本上是啟動了一個線程的協同程序。

暫無
暫無

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

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