TaskCreationOptions.RunContinuationsAsynchronously makes continuations run asynchronously instead of inline inside TaskCompletionSource completion calls. This prevents code after await or ContinueWith callbacks from executing while a lock is held. section 10.2 section 10.7
Use the option when:
- A TrySetResult, TrySetException, or TrySetCanceled call occurs inside a lock. section 10.2
- An asynchronous data structure must keep Enqueue and Dequeue operations fast and avoid running consumer code inside them. section 10.6
- Completion may race with cancellation; prefer TrySetXXX methods because they report whether completion succeeded. section 10.6 section 10.7
Configure the TaskCompletionSource
var tcs = new TaskCompletionSource<int>(TaskCreationOptions.RunContinuationsAsynchronously); section 10.2
public Task<T> Dequeue(CancellationToken cancellationToken)
{
lock (_lock)
{
if (_dataWaitingForProcessors.Count > 0)
{
return Task.FromResult(_dataWaitingForProcessors.Dequeue());
}
var tcs = new TaskCompletionSource<T>(
TaskCreationOptions.RunContinuationsAsynchronously);
_processorsWaitingForData.Enqueue(tcs);
An AsyncQueue creates its waiting TaskCompletionSource with the option while Dequeue holds its lock, then Enqueue completes waiting processors with TrySetResult. section 10.6
