亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

在異步生成器函數中從托兒所內部產生 yield 不好嗎?

在異步生成器函數中從托兒所內部產生 yield 不好嗎?

呼如林 2023-05-09 09:49:43
有人告訴我下面的代碼是不安全的,因為它不允許有一個從 nursery 內部產生的異步生成器,除非它是一個異步上下文管理器。T = TypeVar('T')async def delay(interval: float, source: AsyncIterable[T]) -> AsyncIterable[T]:    """Delays each item in source by an interval.    Received items are temporarily stored in an unbounded queue, along with a timestamp, using    a background task. The foreground task takes items from the queue, and waits until the    item is older than the given interval and then yields it."""    send_channel, receive_channel = trio.open_memory_channel(math.inf)    async def pull_task():        async with aclosing(source) as agen:            async for item in agen:                send_channel.send_nowait((item, trio.current_time() + interval))    async with trio.open_nursery() as nursery:        nursery.start_soon(pull_task)        async with receive_channel:            async for item, timestamp in receive_channel:                now = trio.current_time()                if timestamp > now:                    await trio.sleep(timestamp - now)                yield item我很難理解這怎么可能會破裂。如果有人可以提供使用這個確切的生成器函數的示例代碼,它證明了不安全性,我們將不勝感激和獎勵。上述代碼的目標是在不施加任何背壓的情況下延遲異步序列的處理。如果您能證明此代碼無法像我預期的那樣工作,那我們也將不勝感激。
查看完整描述

1 回答

?
慕姐8265434

TA貢獻1813條經驗 獲得超2個贊

不幸的是,這是正確的——yield在 nursery 或 cancel 范圍內不受支持,除非在用于@contextlib.asynccontextmanager創建異步上下文管理器或編寫異步 pytest fixture 的狹窄情況下。

有幾個原因。其中一些是技術性的:Trio 必須跟蹤哪些 nurseries/cancel 作用域當前在堆棧中處于“活動”狀態,當你離開yield一個時,它會破壞嵌套,Trio 無法知道你已經完成這。(庫無法檢測出yield上下文管理器。)

但還有一個根本的、無法解決的原因,那就是Trio和結構化并發的整個思想是,每個任務都“屬于”一個父任務,如果子任務崩潰,父任務可以收到通知。但是當你yield在一個生成器中時,生成器框架會被凍結并與當前任務分離——它可能會在另一個任務中恢復,或者根本不會恢復。所以當你yield,這打破了托兒所中所有子任務和他們父母之間的聯系。只是沒有辦法將其與結構化并發的原則相協調。

如果我運行以下


async def arange(*args):

? ? for val in range(*args):

? ? ? ? yield val


async def break_it():

? ? async with aclosing(delay(0, arange(3))) as aiter:

? ? ? ? with trio.move_on_after(1):

? ? ? ? ? ? async for value in aiter:

? ? ? ? ? ? ? ? await trio.sleep(0.4)

? ? ? ? ? ? ? ? print(value)


trio.run(break_it)

然后我得到


RuntimeError: Cancel scope stack corrupted: attempted to exit

<trio.CancelScope at 0x7f364621c280, active, cancelled> in <Task

'__main__.break_it' at 0x7f36462152b0> that's still within its child

<trio.CancelScope at 0x7f364621c400, active>


This is probably a bug in your code, that has caused Trio's internal

state to become corrupted. We'll do our best to recover, but from now

on there are no guarantees.


Typically this is caused by one of the following:

? - yielding within a generator or async generator that's opened a cancel

? ? scope or nursery (unless the generator is a @contextmanager or

? ? @asynccontextmanager); see https://github.com/python-trio/trio/issues/638 [...]

通過更改超時和延遲,使超時在生成器內部而不是在生成器外部過期,我還能夠得到一個不同的錯誤:trio.MultiError: Cancelled(), GeneratorExit() raised out of aclosing()


查看完整回答
反對 回復 2023-05-09
  • 1 回答
  • 0 關注
  • 132 瀏覽
慕課專欄
更多

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號