簡體   English   中英

如何在 asyncio 中安全地從列表中刪除值

[英]how to remove value from list safely in asyncio

有一個全局list來存儲數據。 不同的異步函數可能會從中addremove值。

例子:

a = [] # List[Connection]

async def foo():
    for v in a:
        await v.send('msg')
       

async def bar():
    await SomeAsyncFunc()
    a.pop(0)

foobar都會放棄控制權讓其他協程運行,因此在foo中,從列表中刪除值是不安全的。

以下示例顯示了如何為此使用鎖:

創建連接管理器:

import asyncio

class ConnectionsManager:
    def __init__(self, timeout=5):
        self.timeout = timeout
        self._lock = asyncio.Lock()
        self._connections = []
    
    async def __aenter__(self):
        await asyncio.wait_for(self._lock.acquire(), timeout=self.timeout)
        return self._connections

    async def __aexit__(self, *exc):
        self._lock.release()

超時是一種通過循環等待來解決錯誤的安全措施。

管理器可以按如下方式使用:

async def foo():
    for _ in range(10):
        async with cm as connections:
            # do stuff with connection
            await asyncio.sleep(0.25)
            connections.append('foo')
        
async def bar():
    for _ in range(5):
        async with cm as connections:
            # do stuff with connection
            await asyncio.sleep(0.5)
            if len(connections) > 1:
                connections.pop()
            else:
                connections.append('bar')

cm = ConnectionsManager()
t1 = asyncio.create_task(foo())
t2 = asyncio.create_task(bar())
await t1
await t2
async with cm as connections:
    print(connections)

請注意,您也可以在此處更明確地使用連接:

async def foo(cm):
    ...
async def bar(cm):
    ...

只是為了發表評論,為什么與全局變量相比,顯式如此有益。 在某些時候,您可能需要為代碼編寫單元測試,您需要指定函數/方法的所有輸入。 忘記函數隱式輸入的條件(使用的全局變量)很容易導致未經測試的狀態。 例如,您的bar協程需要列表a中的一個元素,如果它為空,它將中斷。 大多數時候它可能會做正確的事情,但有一天在生產中......

暫無
暫無

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

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