1 回答

TA貢獻(xiàn)1786條經(jīng)驗(yàn) 獲得超11個(gè)贊
我的問(wèn)題是為什么
[7] Task delay has been cancelled.
在請(qǐng)求令牌取消的同一線程中執(zhí)行?
這是因?yàn)橛?a >flagawait
安排它的任務(wù)延續(xù)ExecuteSynchronously
。我也認(rèn)為這種行為令人驚訝,并且最初將其報(bào)告為一個(gè)錯(cuò)誤(作為“按設(shè)計(jì)”關(guān)閉)。
更具體地說(shuō),await
捕獲一個(gè)上下文,如果該上下文與正在完成任務(wù)的當(dāng)前上下文兼容,則async
延續(xù)將直接在完成該任務(wù)的線程上執(zhí)行。
要逐步完成它:
某些線程池線程“7”運(yùn)行
cancellationTokenSource.Cancel()
。這會(huì)導(dǎo)致
CancellationTokenSource
進(jìn)入取消狀態(tài)并運(yùn)行其回調(diào)。其中一個(gè)回調(diào)是
Task.Delay
. 該回調(diào)不是特定于線程的,因此它在線程 7 上執(zhí)行。這會(huì)導(dǎo)致
Task
返回的 fromTask.Delay
被取消。已經(jīng)從一個(gè)線程池線程安排了它的await
continuation,線程池線程都被認(rèn)為是相互兼容的,所以async
continuation直接在線程7上執(zhí)行。
提醒一下,線程池線程僅在有代碼要運(yùn)行時(shí)使用。當(dāng)您使用await
to發(fā)送異步代碼時(shí)Task.Run
,它可以在一個(gè)線程上運(yùn)行第一部分(直到await
),然后在另一個(gè)線程上運(yùn)行另一部分(在 之后await
)。
因此,由于線程池線程是可互換的,因此線程 7 在;之后繼續(xù)執(zhí)行該async
方法并不是“錯(cuò)誤的”。await
這只是一個(gè)問(wèn)題,因?yàn)楝F(xiàn)在 之后的代碼在那個(gè)延續(xù)Cancel
上被阻塞了。async
請(qǐng)注意,如果我從主線程執(zhí)行 cancellationTokenSource.Cancel() 那么輸出看起來(lái)像預(yù)期的那樣
這是因?yàn)?UI 上下文被認(rèn)為與線程池上下文不兼容。因此,當(dāng)Task
返回的 fromTask.Delay
被取消時(shí),await
將看到它在 UI 上下文中而不是線程池上下文中,因此它將其繼續(xù)排隊(duì)到線程池而不是直接執(zhí)行它。
有趣的是,當(dāng)我替換
Task.Delay(TimeSpan.FromMinutes(1), cancellationTokenSource.Token)
為cancellationTokenSource.Token.ThrowIfCancellationRequested()
.NET 時(shí),后臺(tái)線程一直處于忙碌狀態(tài),并且輸出再次符合預(yù)期
這不是因?yàn)榫€程“忙”。這是因?yàn)闆](méi)有回調(diào)了。所以觀察方法是輪詢而不是被通知。
該代碼設(shè)置一個(gè)計(jì)時(shí)器(通過(guò)Task.Delay
),然后將線程返回到線程池。當(dāng)定時(shí)器計(jì)時(shí)結(jié)束后,從線程池中抓取一個(gè)線程,檢查取消令牌源是否被取消;如果不是,則設(shè)置另一個(gè)計(jì)時(shí)器并將線程再次返回到線程池。本段的要點(diǎn)是,Task.Run
它不僅僅代表“一個(gè)線程”;它在執(zhí)行代碼時(shí)只有一個(gè)線程(即不在 an 中await
),并且線程可以在任何await
.
除非您混合使用阻塞代碼和異步代碼,否則await
使用的一般問(wèn)題通常不是問(wèn)題。ExecuteSynchronously
在那種情況下,最好的解決方案是將阻塞代碼更改為異步代碼。如果你不能這樣做,那么你需要小心如何繼續(xù)你async
在 之后阻塞的方法await
。這主要是TaskCompletionSource<T>
和的問(wèn)題CancellationTokenSource
。TaskCompletionSource<T>
有一個(gè)很好的RunContinuationsAsynchronously
選項(xiàng)可以覆蓋ExecuteSynchronously
標(biāo)志;不幸的是,CancellationTokenSource
沒(méi)有;你必須使用排隊(duì)你Cancel
對(duì)線程池的調(diào)用Task.Run
。
獎(jiǎng)勵(lì):為您的隊(duì)友準(zhǔn)備的測(cè)驗(yàn)。
- 1 回答
- 0 關(guān)注
- 117 瀏覽
添加回答
舉報(bào)