If in a library I have this
public async DoSomething()
{
await Foo();
}
I then call this like lib.DoSomething().ConfigureAwait(false).GetAwaiter().GetResult()
Would that give me the same benefit as if the library had originally had
public async DoSomething()
{
await Foo().ConfigureAwait(false);
}
No it will not give you the same benefit. If you have:
public async Task DoSomething()
{
await Foo();
}
And then do
lib.DoSomething().ConfigureAwait(false).GetAwaiter().GetResult()
From, for example, UI thread in WPF application - you will deadlock. Before await Foo() you were on UI thread (there was WPF-specific SynchronizationContext.Current) and after await you will try to return back to UI thread. UI thread is blocked on GetResult() so that will result in deadlock. Actually, ConfigureAwait(false) is useless here.
If on the other hand you have
public async Task DoSomething()
{
await Foo().ConfigureAwait(false);
}
And the do
lib.DoSomething().GetAwaiter().GetResult()
No deadlock will happen, because ConfigureAwait(false) tells specifically to not "continue on captured context", so to not return back to SynchronizationContext (UI thread in this case).
Related
In contrast to Task.Wait() or Task.Result, await’ing a Task in C# 5 prevents the thread which executes the wait from lying fallow. Instead, the method using the await keyword needs to be async so that the call of await just makes the method to return a new task which represents the execution of the async method.
But when the await’ed Task completes before the async method has received CPU time again, the await recognizes the Task as finished and thus the async method will return the Task object only at a later time. In some cases this would be later than acceptable because it probably is a common mistake that a developer assumes the await’ing always defers the subsequent statements in his async method.
The mistaken async method’s structure could look like the following:
async Task doSthAsync()
{
var a = await getSthAsync();
// perform a long operation
}
Then sometimes doSthAsync() will return the Task only after a long time.
I know it should rather be written like this:
async Task doSthAsync()
{
var a = await getSthAsync();
await Task.Run(() =>
{
// perform a long operation
};
}
... or that:
async Task doSthAsync()
{
var a = await getSthAsync();
await Task.Yield();
// perform a long operation
}
But I do not find the last two patterns pretty and want to prevent the mistake to occur. I am developing a framework which provides getSthAsync and the first structure shall be common. So getSthAsync should return an Awaitable which always yields like the YieldAwaitable returned by Task.Yield() does.
Unfortunately most features provided by the Task Parallel Library like Task.WhenAll(IEnumerable<Task> tasks) only operate on Tasks so the result of getSthAsync should be a Task.
So is it possible to return a Task which always yields?
First of all, the consumer of an async method shouldn't assume it will "yield" as that's nothing to do with it being async. If the consumer needs to make sure there's an offload to another thread they should use Task.Run to enforce that.
Second of all, I don't see how using Task.Run, or Task.Yield is problematic as it's used inside an async method which returns a Task and not a YieldAwaitable.
If you want to create a Task that behaves like YieldAwaitable you can just use Task.Yield inside an async method:
async Task Yield()
{
await Task.Yield();
}
Edit:
As was mentioned in the comments, this has a race condition where it may not always yield. This race condition is inherent with how Task and TaskAwaiter are implemented. To avoid that you can create your own Task and TaskAwaiter:
public class YieldTask : Task
{
public YieldTask() : base(() => {})
{
Start(TaskScheduler.Default);
}
public new TaskAwaiterWrapper GetAwaiter() => new TaskAwaiterWrapper(base.GetAwaiter());
}
public struct TaskAwaiterWrapper : INotifyCompletion
{
private TaskAwaiter _taskAwaiter;
public TaskAwaiterWrapper(TaskAwaiter taskAwaiter)
{
_taskAwaiter = taskAwaiter;
}
public bool IsCompleted => false;
public void OnCompleted(Action continuation) => _taskAwaiter.OnCompleted(continuation);
public void GetResult() => _taskAwaiter.GetResult();
}
This will create a task that always yields because IsCompleted always returns false. It can be used like this:
public static readonly YieldTask YieldTask = new YieldTask();
private static async Task MainAsync()
{
await YieldTask;
// something
}
Note: I highly discourage anyone from actually doing this kind of thing.
Here is a polished version of i3arnon's YieldTask:
public class YieldTask : Task
{
public YieldTask() : base(() => { },
TaskCreationOptions.RunContinuationsAsynchronously)
=> RunSynchronously();
public new YieldAwaitable.YieldAwaiter GetAwaiter()
=> default;
public new YieldAwaitable ConfigureAwait(bool continueOnCapturedContext)
{
if (!continueOnCapturedContext) throw new NotSupportedException();
return default;
}
}
The YieldTask is immediately completed upon creation, but its awaiter says otherwise. The GetAwaiter().IsCompleted always returns false. This mischief makes the await operator to trigger the desirable asynchronous switch, every time it awaits this task. Actually creating multiple YieldTask instances is redundant. A singleton would work just as well.
There is a problem with this approach though. The underlying methods of the Task class are not virtual, and hiding them with the new modifier means that polymorphism doesn't work. If you store a YieldTask instance to a Task variable, you'll get the default task behavior. This is a considerable drawback for my use case, but I can't see any solution around it.
Regarding the right worker method signature I need to understand the following:
is there a point in returning Task instead of void for Worker method (if going sync)?
Should I really wait (call Wait()) on the Worker method (if going sync)?
what should be the return value of Worker method when marked as returning Task object (both if going sync/async)?
what signature and body of Worker method should be, given the work it completes is long-running CPU/IO-bound work? Should I follow this recommendation (if going mixed/async)?
Note
Despite the cpu-bound code, there's a choice to call async versions of io-bound methods (sql queries). So it may be all sync or partially async. As for the nature of code in the Worker method.
public class LoopingService
{
private CancellationTokenSource cts;
// ..
void Worker(CancellationToken cancellationToken)
{
while(!cancellationToken.IsCancellationRequested)
{
// mixed, CPU/IO-bound code
try {
// sql query (can be called either as sync/async)
var lastId = documentService.GetLastDocument().Id;
// get next document from a public resource (third-party code, sync)
// can be moved to a web api
var document = thirdPartyDocumentService.GetNextDocument(lastId);
// apply different processors in parallel
var tasksList = new List<Task>();
foreach(var processor in documentService.Processors) {
// each processor checks if it's applicable
// which may include xml-parsing, additional db calls, regexes
// if it's applicable then document data is inserted into the db
var task = new Task(() => processor.Process(document));
tasksList.Add(task);
task.Start();
}
// or
// var tasksList = documentService.ProcessParallel(document);
Task.WaitAll(tasksList.ToArray(), cancellationToken);
}
catch(Exception ex) {
logger.log(ex);
}
}
}
public void Start()
{
this.cts = new CancellationTokenSource();
Task.Run(() => this.Worker(cts.Token));
}
public void Stop()
{
this.cts.Cancel();
this.cts.Dispose();
}
}
is there a point in returning Task instead of void for Worker method?
If Worker is a truly asynchronous method it should return a Task for you to be able to await it. If it's just a synchronous method runnning on a background thread there is no point of changing the return type from void provided that the method is not supposed to return anything.
what should be the return value of Worker method when marked as returning Task object?
Nothing. Provided that the method is asynchronous and marked as async with a return type of Task, it shouldn't return any value:
async Task Worker(CancellationToken cancellationToken) { ... }
Note that there is no point of defining the method as async unless you actually use the await keyword in it.
what signature and body of Worker method should be given the work it completes is long-running CPU/IO-bound work? Should I follow this recommendation?
Yes, probably. If you for some reason are doing both asynchronous and synchronous (CPU-bound) work in the same method, you should prefer to using an asynchronous signature but not wrap the synchronous stuff in Task.Run. Then your service would look something like this:
public class LoopingService
{
private CancellationTokenSource cts;
async Task Worker(CancellationToken cancellationToken)
{
while (!cancellationToken.IsCancellationRequested)
{
await ...
}
}
public async Task Start()
{
this.cts = new CancellationTokenSource();
await this.Worker(cts.Token).ConfigureAwait(false);
}
public void Stop()
{
this.cts.Cancel();
this.cts.Dispose();
}
}
Ideally your method should be either asynchronous or CPU-bound but not both though.
This is how my code is setup. However I am getting an exception when updating myUIElement in Method2.
"the calling thread cannot access this object because a different
thread owns it.
Should anything after await always be called on UI thread? What am I doing wrong here?
private async void Method1()
{
// I want to wait until Method2 is completed before doing anything else in Method1
await Task.Factory.StartNew(() => Method2());
}
private async void Method2()
{
// Reading few things from configuration etc
await Task.Factory.StartNew(() => SomeAPILoadDataFromSomewhere());
myUIElement.Text = "something useful has happened";
}
}
You shouldn't be using StartNew when you don't actually want the code in question to run in a non-UI thread. Just remove it entirely.
Also note that you should only have async void methods as top level event handlers. Any async method that you intend to await should return a Task.
You should also generally be using Task.Run instead of StartNew where possible.
//This should return a Task and not void if it is used by another asynchronous method
private async void Method1()
{
await Method2();
DoSomethingElse();
}
private async Task Method2()
{
await Task.Run(() => SomeAPILoadDataFromSomewhere());
myUIElement.Text = "something useful has happened";
}
Does TaskScheduler.Default not always guarantee the task will be executed on a pool thread?
While fixing a bug, I found at least one case when it doesn't. It can be reproduced like this (a contrived example made from real code):
var tcs = new TaskCompletionSource<bool>();
var sc = SynchronizationContext.Current;
sc.Post(_ => tcs.SetResult(true), null);
await tcs.Task.ContinueWith(_ =>
{
// breaks here
Debug.Assert(Thread.CurrentThread.IsThreadPoolThread);
},
CancellationToken.None,
TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default);
Are there any other cases?
Also, is there an elegant way to make sure the ContinueWith action is executed synchronously, if the antecedent task has completed on a pool thread, or gets queued to thread pool otherwise (I know I can use QueueUserWorkItem in ContinueWith action, but I don't like it).
EDITED, I guess I can implement my own TaskScheduler and check if I am already on a thread pool thread inside TryExecuteTaskInline, to control this.
I think the reason this happens is because you use the TaskContinuationOptions.ExecuteSynchronously. From the doco:
ExecuteSynchronously Specifies that the continuation task should be
executed synchronously. With this option specified, the continuation
will be run on the same thread that causes the antecedent task to
transition into its final state. If the antecedent is already complete
when the continuation is created, the continuation will run on the
thread creating the continuation. Only very short-running
continuations should be executed synchronously.
If the antecedent task completes on a thread other than a thread pool thread then the continuation will run on that thread also. So I guess in that case no scheduling occurs. Another case might be when the continuation task is already complete, which will also run synchronously.
Update
To achieve what you are asking in your second part of the question I think you'll need a custom awaiter. See this article for more details but something like this might work for you:
public static class Extensions
{
public static ThreadPoolTaskAwaiter WithThreadPool(this Task task)
{
return new ThreadPoolTaskAwaiter(task);
}
public class ThreadPoolTaskAwaiter : INotifyCompletion
{
private readonly TaskAwaiter m_awaiter;
public ThreadPoolTaskAwaiter(Task task)
{
if (task == null) throw new ArgumentNullException("task");
m_awaiter = task.GetAwaiter();
}
public ThreadPoolTaskAwaiter GetAwaiter() { return this; }
public bool IsCompleted { get { return m_awaiter.IsCompleted; } }
public void OnCompleted(Action continuation)
{
if (Thread.CurrentThread.IsThreadPoolThread)
{
continuation();
}
else
{
Task.Run(continuation);
}
}
public void GetResult()
{
m_awaiter.GetResult();
}
}
}
Then you use it like this:
public static async Task Execute()
{
await Task.Delay(500).WithThreadPool();
// does not break here
if (!Thread.CurrentThread.IsThreadPoolThread)
{
Debugger.Break();
}
}
I tried to use the SwitchTo method today to switch to the GUI thread, and found that the example I lifted it from does not work, simply because the method is not there.
I then found this blurb here:
The reason we got rid of it was because it was so dangerous. The alternative is to bundle up your code inside TaskEx.Run...
My question is simply: Why was it dangerous? What specific dangers would using it lead to?
Note that I did read the rest of that post, so I do understand there are technical limitations here. My question is still, if I'm aware of this, why is it dangerous?
I am considering reimplementing helper methods to give me the specified functionality, but if there is something fundamentally broken, other than that someone decided it was dangerous, I would not do it.
Specifically, very naively, here's how I would consider implementing the required methods:
public static class ContextSwitcher
{
public static ThreadPoolContextSwitcher SwitchToThreadPool()
{
return new ThreadPoolContextSwitcher();
}
public static SynchronizationContextSwitcher SwitchTo(this SynchronizationContext synchronizationContext)
{
return new SynchronizationContextSwitcher(synchronizationContext);
}
}
public class SynchronizationContextSwitcher : INotifyCompletion
{
private readonly SynchronizationContext _SynchronizationContext;
public SynchronizationContextSwitcher(SynchronizationContext synchronizationContext)
{
_SynchronizationContext = synchronizationContext;
}
public SynchronizationContextSwitcher GetAwaiter()
{
return this;
}
public bool IsCompleted
{
get
{
return false;
}
}
public void OnCompleted(Action action)
{
_SynchronizationContext.Post(_ => action(), null);
}
public void GetResult()
{
}
}
public class ThreadPoolContextSwitcher : INotifyCompletion
{
public ThreadPoolContextSwitcher GetAwaiter()
{
return this;
}
public bool IsCompleted
{
get
{
return false;
}
}
public void OnCompleted(Action action)
{
ThreadPool.QueueUserWorkItem(_ => action(), null);
}
public void GetResult()
{
}
}
This would allow me to write code like this:
public async void Test()
{
await ContextSwitcher.SwitchToThreadPool(); // ensure we're not bogging down the UI thread
// do some heavy processing
await _UIContext.SwitchTo(); // presumably saved from the main thread
// update UI with new data
}
Stephen Toub has some more information on the reasoning in this thread.
To summarize, it's not a good idea for two reasons:
It promotes unstructured code. If you have "heavy processing" that you need to do, it should be placed in a Task.Run. Even better, separate your business logic from your UI logic.
Error handling and (some) continuations run in an unknown context. catch/finally blocks in Test would need to handle running in a thread pool or UI context (and if they're running in the thread pool context, they can't use SwitchTo to jump on the UI context). Also, as long as you await the returned Task you should be OK (await will correct the continuation context if necessary), but if you have explicit ContinueWith continuations that use ExecuteSynchronously, then they'll have the same problem as the catch/finally blocks.
In short, the code is cleaner and more predictable without SwitchTo.
ConfigureAwait is actually more dangerous than SwitchTo. Mentally tracking the current context and the last SwitchTo call is no more difficult than tracking where a variable was last assigned. On the other hand, ConfigureAwait switches context if and only if the call actually ran asynchronously. If the task was already completed, the context is preserved. You have no control over this.
It's 2020 and it looks like SwitchTo is set to come back to CLR soon, according to David Fowler and Stephen Toub in this GitHub issue, as there's no more limitations for await inside try/catch.
IMO, using something like await TaskScheduler.Default.SwitchTo() explicitly is better than relying upon ConfigureAwait(false) in the 3rd party library code, especially if we want to make sure that code doesn't execute on any custom synchronization context. I have a blog post with more details on that, including an experimental implementation of SwitchTo.
In a nutshell, I believe the first option from the below clearly indicates the intent, with minimum boilerplate code:
// switch to the thread pool explicitly for the rest of the async method
await TaskScheduler.Default.SwitchTo();
await RunOneWorkflowAsync();
await RunAnotherWorkflowAsync();
// execute RunOneWorkflowAsync on the thread pool
// and stay there for the rest of the async method
await Task.Run(RunOneWorkflowAsync).ConfigureAwait(false);
await RunAnotherWorkflowAsync();
await Task.Run(async () =>
{
// start on the thread pool
await RunOneWorkflowAsync();
await RunAnotherWorkflowAsync();
}).ConfigureAwait(false);
// continue on the thread pool for the rest of the async method
// start on whatever the current synchronization context is
await RunOneWorkflowAsync().ConfigureAwait(false);
// continue on the thread pool for the rest of the async method,
// unless everything inside `RunOneWorkflowAsync` has completed synchronously
await RunAnotherWorkflowAsync();
The SwitchTo extension method is available in the Microsoft.VisualStudio.Threading package. Here is the signature of this method:
public static
Microsoft.VisualStudio.Threading.AwaitExtensions.TaskSchedulerAwaitable
SwitchTo(this System.Threading.Tasks.TaskScheduler scheduler,
bool alwaysYield = false);
And here is an example of how to use it:
using Microsoft.VisualStudio.Threading;
private async void Button_Click(object sender, EventArgs e)
{
var ui = TaskScheduler.FromCurrentSynchronizationContext(); // Capture the UI thread
// Do something on the UI thread
await TaskScheduler.Default.SwitchTo(); // Switch to the ThreadPool
// Do something on the ThreadPool
await ui.SwitchTo(); // Switch back to the UI thread
// Do something on the UI thread
}