Исходя из Task.Run не выполняется и после прочтения TaskContinuationOptions.RunContinuationsAsynchronously и Stack Dives , я хотел подтвердить, что получил это право:
При использовании для TaskCompletionSource
:
Если установлено TaskCreationOptions.RunContinuationsAsynchronously
, TaskCompletionSource.SetResult()
отправит все продолжения в пул потоков, где они будут ставятся в очередь и обрабатываются, может быть, похоже на их упаковку в вызовы Task.Run()
. Продолжения будут обрабатываться асинхронно, в параллельных потоках пула потоков, не блокируя текущий поток / задачу.
Если TaskCreationOptions.RunContinuationsAsynchronously
установлено , а не установлено (по умолчанию, Я предполагаю), тогда все продолжения (= делегаты в вызовах Task.ContinueWith()
) выполняются синхронно, один за другим в потоке, который вызвал TaskCompletionSource.SetResult()
, блокируя текущий поток / задачу до тех пор, пока все продолжения не будут обработаны, возможно, аналогично как обрабатываются делегаты событий.
Я написал следующий тест, чтобы попытаться подтвердить эти предположения:
[TestMethod]
public void TestTaskCreationOptions()
{
void callback(int i, int sleep)
{
Console.WriteLine($"{i} {System.Threading.Thread.CurrentThread.Description()} - before sleep {sleep}");
System.Threading.Thread.Sleep(sleep);
Console.WriteLine($"{i} {System.Threading.Thread.CurrentThread.Description()} - after sleep {sleep}");
}
void test(TaskCompletionSource<object> tcs)
{
Console.WriteLine($"starting: {tcs.Task.CreationOptions}");
tcs.Task.ContinueWith((t) => callback(1,1000));
tcs.Task.ContinueWith((t) => callback(2,300));
tcs.Task.ContinueWith((t) => callback(3,300));
Task.Run(() =>
{
callback(-1,100);
tcs.SetResult(null);
callback(-2,100);
});
Console.WriteLine("waiting");
Thread.Sleep(5000);
Console.WriteLine("done");
}
test(new TaskCompletionSource<object>( TaskCreationOptions.RunContinuationsAsynchronously));
test(new TaskCompletionSource<object>());
}
И вот (аннотированные) результаты:
starting: RunContinuationsAsynchronously
waiting
-1 [11, Back, Pool] - before sleep 100
-1 [11, Back, Pool] - after sleep 100
-2 [11, Back, Pool] - before sleep 100
1 [12, Back, Pool] - before sleep 1000 --> continuations are correctly executed on different threads
2 [15, Back, Pool] - before sleep 300
3 [13, Back, Pool] - before sleep 300
-2 [11, Back, Pool] - after sleep 100 --> continuations correctly do not block the calling task/thread
3 [13, Back, Pool] - after sleep 300
2 [15, Back, Pool] - after sleep 300
1 [12, Back, Pool] - after sleep 1000
done
starting: None
-1 [13, Back, Pool] - before sleep 100
waiting
-1 [13, Back, Pool] - after sleep 100
-2 [13, Back, Pool] - before sleep 100
1 [15, Back, Pool] - before sleep 1000 --> continuations are incorrectly(?!) executed on different threads
2 [14, Back, Pool] - before sleep 300
3 [16, Back, Pool] - before sleep 300
-2 [13, Back, Pool] - after sleep 100
2 [14, Back, Pool] - after sleep 300 --> continuations incorrectly(?!) do not block the calling task/thread
3 [16, Back, Pool] - after sleep 300
1 [15, Back, Pool] - after sleep 1000
done
Итак, по-видимому, мое второе предположение (без указания флага) было неверным: продолжения не выполняются синхронно - по крайней мере, не так, как я понимаю термин; они выполняются в отдельных потоках, поэтому они не блокируют вызывающий поток или друг друга.
Почему?