在我的應用程式中,我需要安排一些Action延遲的執行。就像setTimeout在 JavaScript 中一樣。此外,當應用程式執行結束時,我需要取消所有尚未執行的計劃執行。所以我必須在Task.Delay沒有等待的情況下呼叫并傳遞CancellationToken給它。但是,如果我這樣做,我將面臨記憶體泄漏:CancellationTokenSource CallbackNode在我呼叫之前,沒有一個會被處理,Cancel并且我從中獲取s 傳遞給.DisposeCancellationTokenSourceCancellationTokenTask.Delay
最小的可重現示例:
CancellationTokenSource cts = new CancellationTokenSource();
for (int i = 0; i < 1000; i )
{
Task.Delay(500, cts.Token).ContinueWith(_ => Console.WriteLine("Scheduled action"));
}
await Task.Delay(1000);
Console.ReadLine();
執行此示例后,它會留下 1000 個CancellationTokenSource CallbackNode.
如果我在沒有出現泄漏
后寫
cts.Cancel()await Task.Delay(1000);
Why this leak happens? All Tasks was completed, so there should not be references to cts.Token. Disposing passed Task to continuation action does not help.
Also, if I await Task that schedule execution of action, leak does not appear.
uj5u.com熱心網友回復:
我有點驚訝,但看起來這是故意的。當注冊被取消時,CancellationTokenSource仍然將 的實體保留CancellationTokenSource CallbackNode在空閑串列中,以便在下一次回呼中重用它。這是一個自以為是的優化,可能會適得其反。
為了說明這一點,請嘗試連續運行兩次測驗:
var tasks = new Task[1000];
for (int i = 0; i < 1000; i )
{
tasks[i] = Task.Delay(500, cts.Token).ContinueWith(_ => Console.WriteLine("Scheduled action"));
}
await Task.WhenAll(tasks);
for (int i = 0; i < 1000; i )
{
tasks[i] = Task.Delay(500, cts.Token).ContinueWith(_ => Console.WriteLine("Scheduled action"));
}
await Task.WhenAll(tasks);
Console.ReadLine()
您將看到仍然有 1000 個實體CancellationTokenSource CallbackNode,盡管總共注冊了 2000 個回呼。這是因為第二次迭代重用了第一次迭代中創建的節點。
您對此無能為力,我相信這是設計使然。在任何情況下,記憶體量幾乎可以忽略不計(在大多數x情況下,x同時注冊的回呼的數量是多少)。
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/443540.html
標籤:c# asynchronous async-await memory-leaks cancellation-token
