1 回答

TA貢獻1813條經(jīng)驗 獲得超2個贊
不幸的是,這是正確的——yield
在 nursery 或 cancel 范圍內(nèi)不受支持,除非在用于@contextlib.asynccontextmanager
創(chuàng)建異步上下文管理器或編寫異步 pytest fixture 的狹窄情況下。
有幾個原因。其中一些是技術(shù)性的:Trio 必須跟蹤哪些 nurseries/cancel 作用域當(dāng)前在堆棧中處于“活動”狀態(tài),當(dāng)你離開yield
一個時,它會破壞嵌套,Trio 無法知道你已經(jīng)完成這。(庫無法檢測出yield
上下文管理器。)
但還有一個根本的、無法解決的原因,那就是Trio和結(jié)構(gòu)化并發(fā)的整個思想是,每個任務(wù)都“屬于”一個父任務(wù),如果子任務(wù)崩潰,父任務(wù)可以收到通知。但是當(dāng)你yield
在一個生成器中時,生成器框架會被凍結(jié)并與當(dāng)前任務(wù)分離——它可能會在另一個任務(wù)中恢復(fù),或者根本不會恢復(fù)。所以當(dāng)你yield
,這打破了托兒所中所有子任務(wù)和他們父母之間的聯(lián)系。只是沒有辦法將其與結(jié)構(gòu)化并發(fā)的原則相協(xié)調(diào)。
如果我運行以下
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 [...]
通過更改超時和延遲,使超時在生成器內(nèi)部而不是在生成器外部過期,我還能夠得到一個不同的錯誤:trio.MultiError: Cancelled(), GeneratorExit() raised out of aclosing()
添加回答
舉報