I would like to ask you on your opinion about the correct architecture when to use Task.Run. I am experiencing laggy UI in our WPF .NET 4.5
application (with Caliburn Micro framework).
Basically I am doing (very simplified code snippets):
public class PageViewModel : IHandle<SomeMessage>
{
...
public async void Handle(SomeMessage message)
{
ShowLoadingAnimation();
// Makes UI very laggy, but still not dead
await this.contentLoader.LoadContentAsync();
HideLoadingAnimation();
}
}
public class ContentLoader
{
public async Task LoadContentAsync()
{
await DoCpuBoundWorkAsync();
await DoIoBoundWorkAsync();
await DoCpuBoundWorkAsync();
// I am not really sure what all I can consider as CPU bound as slowing down the UI
await DoSomeOtherWorkAsync();
}
}
From the articles/videos I read/saw, I know that await async is not necessarily running on a background thread and to start work in the background you need to wrap it with await Task.Run(async () => ... ). Using async await does not block the UI, but still it is running on the UI thread, so it is making it laggy.
Where is the best place to put Task.Run?
Should I just
Wrap the outer call because this is less threading work for .NET
, or should I wrap only CPU-bound methods internally running with Task.Run as this makes it reusable for other places? I am not sure here if starting work on background threads deep in core is a good idea.
Ad (1), the first solution would be like this:
public async void Handle(SomeMessage message)
{
ShowLoadingAnimation();
await Task.Run(async () => await this.contentLoader.LoadContentAsync());
HideLoadingAnimation();
}
// Other methods do not use Task.Run as everything regardless
// if I/O or CPU bound would now run in the background.
Ad (2), the second solution would be like this:
public async Task DoCpuBoundWorkAsync()
{
await Task.Run(() => {
// Do lot of work here
});
}
public async Task DoSomeOtherWorkAsync(
{
// I am not sure how to handle this methods -
// probably need to test one by one, if it is slowing down UI
}
Note the guidelines for performing work on a UI thread, collected on my blog:
Don't block the UI thread for more than 50ms at a time.
You can schedule ~100 continuations on the UI thread per second; 1000 is too much.
There are two techniques you should use:
1) Use ConfigureAwait(false) when you can.
E.g., await MyAsync().ConfigureAwait(false); instead of await MyAsync();.
ConfigureAwait(false) tells the await that you do not need to resume on the current context (in this case, "on the current context" means "on the UI thread"). However, for the rest of that async method (after the ConfigureAwait), you cannot do anything that assumes you're in the current context (e.g., update UI elements).
For more information, see my MSDN article Best Practices in Asynchronous Programming.
2) Use Task.Run to call CPU-bound methods.
You should use Task.Run, but not within any code you want to be reusable (i.e., library code). So you use Task.Run to call the method, not as part of the implementation of the method.
So purely CPU-bound work would look like this:
// Documentation: This method is CPU-bound.
void DoWork();
Which you would call using Task.Run:
await Task.Run(() => DoWork());
Methods that are a mixture of CPU-bound and I/O-bound should have an Async signature with documentation pointing out their CPU-bound nature:
// Documentation: This method is CPU-bound.
Task DoWorkAsync();
Which you would also call using Task.Run (since it is partially CPU-bound):
await Task.Run(() => DoWorkAsync());
One issue with your ContentLoader is that internally it operates sequentially. A better pattern is to parallelize the work and then sychronize at the end, so we get
public class PageViewModel : IHandle<SomeMessage>
{
...
public async void Handle(SomeMessage message)
{
ShowLoadingAnimation();
// makes UI very laggy, but still not dead
await this.contentLoader.LoadContentAsync();
HideLoadingAnimation();
}
}
public class ContentLoader
{
public async Task LoadContentAsync()
{
var tasks = new List<Task>();
tasks.Add(DoCpuBoundWorkAsync());
tasks.Add(DoIoBoundWorkAsync());
tasks.Add(DoCpuBoundWorkAsync());
tasks.Add(DoSomeOtherWorkAsync());
await Task.WhenAll(tasks).ConfigureAwait(false);
}
}
Obviously, this doesn't work if any of the tasks require data from other earlier tasks, but should give you better overall throughput for most scenarios.
Related
Consider an API that returns Tasks with some values.
I want to update the UI based on that values in parallel (when one of the values is ready I want to update it without waiting for the second one assuming the update of each value as its own update method).
public async Task MyFunc()
{
Task<First> firstTask = MyAPI.GetFirstValue();
Task<Second> secondTask = MyAPI.GetSecondValue();
UpdateFirstValueUI(await firstTask)
UpdateSecondValueUI(await secondTask)
}
the code example will wait for the first value, update the UI, wait for the second value and update the UI again.
What is the best practice for that scenario? I was wondering if ContinueWith is best practice because I mostly see it in legacy code (before there was async-await).
edit with a better example:
assuming we have two implementations of that API and the code looks like that
public async Task MyFunc()
{
Task<First> firstTask = null
Task<Second> secondTask = null
if (someCondition)
{
firstTask = MyAPI1.GetFirstValue();
secondTask = MyAPI1.GetSecondValue();
}
else
{
firstTask = MyAPI2.GetFirstValue();
secondTask = MyAPI2.GetSecondValue();
}
UpdateFirstValueUI(await firstTask)
UpdateSecondValueUI(await secondTask)
}
now as you see I don't want call the update methods in two different branches (assuming we split that method for each API after the branching)
so looking for a way to change only the update calls so they could happen in parallel
The ContinueWith is a primitive method that has some rare uses in library code, and should generally be avoided in application code. The main problem with using the ContinueWith in your case is that it's going to execute the continuation on a ThreadPool, which is not what you want, because your intention is to update the UI. And updating the UI from any other thread than the UI thread is a no no. It is possible to solve this¹ problem by configuring the ContinueWith with a suitable TaskScheduler, but it's much simpler to solve it with async/await composition. My suggestion is to add the Run method below in some static class in your project:
public static class UF // Useful Functions
{
public static async Task Run(Func<Task> action) => await action();
}
This method just invokes and awaits the supplied asynchronous delegate. You could use this method to combine your asynchronous API calls with their UI-updating continuations like this:
public async Task MyFunc()
{
Task<First> task1;
Task<Second> task2;
if (someCondition)
{
task1 = MyAPI1.GetFirstValueAsync();
task2 = MyAPI1.GetSecondValueAsync();
}
else
{
task1 = MyAPI2.GetFirstValueAsync();
task2 = MyAPI2.GetSecondValueAsync();
}
Task compositeTask1 = UF.Run(async () => UpdateFirstValueUI(await task1));
Task compositeTask2 = UF.Run(async () => UpdateSecondValueUI(await task2));
await Task.WhenAll(compositeTask1, compositeTask2);
}
This will ensure that the UI will be updated immediately after each asynchronous operation completes.
As a side note, if you have any suspicion that the MyAPI asynchronous methods may contain blocking code, you could offload them to the ThreadPool by using the Task.Run method, like this:
task1 = Task.Run(() => MyAPI1.GetFirstValueAsync());
For a thorough explanation about why this is a good idea, you can check out this answer.
The difference between the built-in Task.Run method and the custom UF.Run method presented above, is that the Task.Run invokes the asynchronous delegate on the ThreadPool, while the UF.Run invokes it on the current thread. If you have any idea about a better name than Run, please suggest. :-)
¹ The ContinueWith comes with a boatload of other problems as well, like wrapping errors in AggregateExceptions, making it easy to swallow exceptions by mistake, making it hard to propagate the IsCanceled status of the antecedent task, making it trivial to leak fire-and-forget tasks, requiring to Unwrap nested Task<Task>s created by async delegates etc.
Assuming I have an async builder method and I need to convert a collection of db models into a collection of UI models, I wrote the code below (simplified for example).
After reading this medium article, the code was refactoring to bubble ASYNC all the way up but I'd still like to know
will collection.GetConsumingEnumerable() block, leading to deadlocks, similar to .Wait or .Result
what is the preferred way to stream items in an async method
public IEnumerable<GenericDevice> GetGenericDevices(SearchRequestModel srModel)
{
var collection = new BlockingCollection<GenericDevice>(new ConcurrentBag<GenericDevice>());
Task.Run(() => QueueGenericDevices(collection, srModel));
foreach (var genericDevice in collection.GetConsumingEnumerable())
{
yield return genericDevice;
}
}
private async Task QueueGenericDevices(BlockingCollection<GenericDevice> collection, SearchRequestModel srModel)
{
var efDevices = _dbDeviceRepo.GetEfDevices(srModel);
var tasks = efDevices
.Select(async efDevice =>
{
var genericDevice = await BuildGenericDevice(efDevice, srModel);
collection.Add(genericDevice);
});
await Task.WhenAll(tasks);
collection.CompleteAdding();
}
will collection.GetConsumingEnumerable() block, leading to deadlocks, similar to .Wait or .Result
No. There are two parts to the classic deadlock:
A context that only allows one thread at a time (usually a UI SynchronizationContext or ASP.NET pre-Core SynchronizationContext).
Blocking on code that uses await (which uses that context in order to complete the async method).
GetConsumingEnumerable is a blocking call, but it's not blocking on asynchronous code, so there's no danger of the classic deadlock there. More generally, using a queue like this inserts a "barrier" of sorts between two pieces of code.
what is the preferred way to stream items in an async method
Modern code should use IAsyncEnumerable<T>.
However, if you're not on that platform yet, you can use a queue as a "barrier". You wouldn't want to use BlockingCollection<T>, though, because it is designed for synchronous producers and synchronous consumers. For asynchronous producers/consumers, I'd recommend Channels.
I have got a simple program for async and await:
class Program
{
static void Main()
{
var demo = new AsyncAwaitDemo();
Task.Run(() => {demo.DoStuff()};
while (true)
{
Console.WriteLine("Doing Stuff on the Main Thread...................");
}
}
}
public class AsyncAwaitDemo
{
public async Task DoStuff()
{
await LongRunningOperation();
}
private static async Task<string> LongRunningOperation()
{
int counter;
for (counter = 0; counter < 50000; counter++)
{
Console.WriteLine(counter);
}
return "Counter = " + counter;
}
}
My first question is :
Is it mandatory to use Task.Run in any async and await method because I just removed and the program becomes synchronous.I am aware of the fact that await is used to create suspension points and doesnt get executed further until the awaited function completes for that thread.
Secondly,
How many number of threads while the following of code creates?
Task.Run(() => {demo.DoStuff()};
If it is more than one , then is the number of threads dependent on the inner operation , in this example it is for loop with counter .
I am beginning to understand the asynchronous programming in C# , read and practiced few samples . I am able to get the context almost apart from the clarifications I asked as above.
async/await has nothing to do with Task.Run; however, they do work well together in some scenarios.
Task.Run runs some code on a thread pool thread. async/await is a code transformation that allows you to write asynchronous code in a more natural, imperative manner (see my async intro for more details about how async works).
Is it mandatory to use Task.Run in any async and await method because I just removed and the program becomes synchronous.
Not at all! async/await are very commonly used without Task.Run. Anytime that you have a truly asynchronous operation, then you can use async/await. Generally speaking, anything I/O-based is a good example of a naturally asynchronous operation. So, you can use something like HttpClient.GetStringAsync using just async/await without any Task.Run at all.
Note, however, that it is almost always wrong to use async without await. The compiler will give you a warning for LongRunningOperation, informing you of the fact that it's not actually asynchronous.
How many number of threads while the following of code creates? Task.Run(() => {demo.DoStuff()};
That code just queues DoStuff to the thread pool. A thread pool thread will execute DoStuff. Thread creation is dependent on a lot of other factors.
Suppose I have this function:
void DoWork()
{
//do long work
}
I want this function to be executed on a different thread.
I'm used to use threads, so one way to execute it on a thread is:
new Thread(DoWork).Start();
I'm trying to learn the new task/await C# feature so I modified the method to
async Task DoWork()
{
await Task.Run(delegate() { /*do stuff*/});
}
Now I can use it this way:
DoWork().Wait(0);
Or I can modify the method again to:
async void DoWork()
{
await Task.Run(delegate() { /*do stuff*/});
}
And now just call:
DoWork();
Which solution should I use ?
I do not need the result of the task nor waiting for it to finish.
The answer is neither. You are creating a trivial asynchronous wrapper around synchronous logic.
Stephen Cleary: using Task.Run for asynchronous wrappers is a code smell
Stephen Toub (Microsoft): Should I expose asynchronous wrappers for synchronous methods? (and no, you shouldn't).
The async part of your DoWork method adds no value. It diminishes the value of your API by making it impossible to execute your potentially complex "do stuff" logic in any way other than fire-and-forget.
The correct approach is to ditch Task.Run and strip your method down to the bare essentials:
void DoWork()
{
// Do long CPU-bound work, synchronously
}
...
// Then, somewhere in the calling code:
Task.Run(() => DoWork()); // Fire and forget unless you await the resulting Task or block on it.
Task.Run is currently the preferred method of scheduling work on the thread pool (as of .NET 4.6.x)
I have a method in my view model
private async void SyncData(SyncMessage syncMessage)
{
if (syncMessage.State == SyncState.SyncContacts)
{
this.SyncContacts();
}
}
private async Task SyncContacts()
{
foreach(var contact in this.AllContacts)
{
// do synchronous data analysis
}
// ...
// AddContacts is an async method
CloudInstance.AddContacts(contactsToUpload);
}
When I call SyncData from the UI commands and I'm syncing a large chunk of data UI freezes. But when I call SyncContacts with this approach
private void SyncData(SyncMessage syncMessage)
{
if (syncMessage.State == SyncState.SyncContacts)
{
Task.Run(() => this.SyncContacts());
}
}
Everything is fine. Should not they be the same?
I was thinking that not using await for calling an async method creates a new thread.
Should not they be the same? I was thinking that not using await for
calling an async method creates a new thread.
No, async does not magically allocate a new thread for it's method invocation. async-await is mainly about taking advantage of naturally asynchronous APIs, such as a network call to a database or a remote web-service.
When you use Task.Run, you explicitly use a thread-pool thread to execute your delegate. If you mark a method with the async keyword, but don't await anything internally, it will execute synchronously.
I'm not sure what your SyncContacts() method actually does (since you haven't provided it's implementation), but marking it async by itself will gain you nothing.
Edit:
Now that you've added the implementation, i see two things:
I'm not sure how CPU intensive is your synchronous data analysis, but it may be enough for the UI to get unresponsive.
You're not awaiting your asynchronous operation. It needs to look like this:
private async Task SyncDataAsync(SyncMessage syncMessage)
{
if (syncMessage.State == SyncState.SyncContacts)
{
await this.SyncContactsAsync();
}
}
private Task SyncContactsAsync()
{
foreach(var contact in this.AllContacts)
{
// do synchronous data analysis
}
// ...
// AddContacts is an async method
return CloudInstance.AddContactsAsync(contactsToUpload);
}
What your line Task.Run(() => this.SyncContacts()); really does is creating a new task starting it and returning it to the caller (which is not used for any further purposes in your case). That's the reason why it will do its work in the background and the UI will keep working. If you need to (a)wait for the task to complete, you could use await Task.Run(() => this.SyncContacts());. If you just want to ensure that SyncContacts has finished when you return your SyncData method, you could using the returning task and awaiting it at the end of your SyncData method. As it has been suggested in the comments: If you're not interested in whether the task has finished or not you just can return it.
However, Microsoft recommend to don't mix blocking code and async code and that async methods end with Async (https://msdn.microsoft.com/en-us/magazine/jj991977.aspx). Therefore, you should consider renaming your methods and don't mark methods with async, when you don't use the await keyword.
Just to clarify why the UI freezes - the work done in the tight foreach loop is likely CPU-bound and will block the original caller's thread until the loop completes.
So, irrespective of whether the Task returned from SyncContacts is awaited or not, the CPU bound work prior to calling AddContactsAsync will still occur synchronously on, and block, the caller's thread.
private Task SyncContacts()
{
foreach(var contact in this.AllContacts)
{
// ** CPU intensive work here.
}
// Will return immediately with a Task which will complete asynchronously
return CloudInstance.AddContactsAsync(contactsToUpload);
}
(Re : No why async / return await on SyncContacts- see Yuval's point - making the method async and awaiting the result would have been wasteful in this instance)
For a WPF project, it should be OK to use Task.Run to do the CPU bound work off the calling thread (but not so for MVC or WebAPI Asp.Net projects).
Also, assuming the contactsToUpload mapping work is thread-safe, and that your app has full usage of the user's resources, you could also consider parallelizing the mapping to reduce overall execution time:
var contactsToUpload = this.AllContacts
.AsParallel()
.Select(contact => MapToUploadContact(contact));
// or simpler, .Select(MapToUploadContact);