Reputation: 15101
Assume I have a library as follows for both non-UI based apps (ASP.NET) and UI based apps (WinForm and WPF). Unfortunately I cannot avoid mixing IO-bound work and CPU-bound work but I let the consumers invoke DummyWorkAsync
via Task.Run
or not based on their application types (non-UI or UI based apps).
class DummyService
{
public static async Task<int> DummyWorkAsync()
{
// Do some I/O first.
await Task.Delay(1000);
// Tons of work to do in here!
for (int i = 0; i != 10000000; ++i)
;
// Possibly some more I/O here.
await Task.Delay(1000);
// More work.
for (int i = 0; i != 10000000; ++i)
;
return 0;
}
}
This allows UI-based consumer to properly use Task.Run
to call the service, while ASP.NET clients would just call the method directly as follows.
private async void MyButton_Click(object sender, EventArgs e)
{
await Task.Run(() => DummyService.DummyWorkAsync());
}
public class Home: Controller
{
public async Task<ActionResult> IndexAsync()
{
var result = await DummyService.DummyWorkAsync();
return View(result);
}
}
I am interested in the UI-based apps. Is there any difference if I use
private async void MyButton_Click(object sender, EventArgs e)
{
await Task.Run(async () => await DummyService.DummyWorkAsync());
}
rather than
private async void MyButton_Click(object sender, EventArgs e)
{
await Task.Run(() => DummyService.DummyWorkAsync());
}
?
Upvotes: 2
Views: 116
Reputation: 101483
No, there is no (significant) difference. You are basically asking what's the difference between
static Task<int> Something() {
return SomethingAsync();
}
and
static async Task<int> Something() {
return await SomethingAsync();
}
Except in your case there is one more wrapping task (Task.Run
), so differences in exceptions propagation for example which you may find in another questions on the same issue are not relevant in this case.
Because of that - just use
await Task.Run(() => DummyService.DummyWorkAsync());
That said, reasons for doing that are not that clear. Since your IO goes first - you can use ConfigureAwait(false)
to prevent returning control to current synchronization context (which is good practice for libraries anyway). In that case, after your first await the rest (heavy CPU work) will be executed on thread pool thread (with default scheduler at least), so you won't need to use Task.Run
in the first place:
public static async Task<int> DummyWorkAsync()
{
// Do some I/O first.
await Task.Delay(1000).ConfigureAwait(false);
// Tons of work to do in here!
// no problem, we are not on UI thread anyway
for (int i = 0; i != 10000000; ++i)
;
// Possibly some more I/O here.
await Task.Delay(1000).ConfigureAwait(false);
// More work.
for (int i = 0; i != 10000000; ++i)
;
return 0;
}
Upvotes: 3