I'm trying to understand how to use the .net 4.5 async/await keywords with a Task that at it's core is synchronous. I.e. some sort of complex math calculation. I've used a Thread.Sleep to simulate that action in the example below. My question is there a way you can make such a method act like an async method? If not do you just need to do what I did in the ThisWillRunAsyncTest method and do something like Task.Factory.StartNew on that sync method. Is there a cleaner way of doing this?
using System.Threading;
using System.Collections.Generic;
using System.Threading.Tasks;
using NUnit.Framework;
[TestFixture]
public class AsyncAwaitTest
{
[Test]
//This test will take 1 second to run because it runs asynchronously
//Is there a better way to start up a synchronous task and have it run in parallel.
public async void ThisWillRunAsyncTest()
{
var tasks = new List<Task>();
for (int i = 0; i < 5; i++)
{
tasks.Add(Task.Factory.StartNew(() => this.RunTask()));
}
await Task.WhenAll(tasks);
}
[Test]
//This test will take 5 seconds to run because it runs synchronously.
//If the Run Task had an await in it, this this would run synchronously.
public async void ThisWillRunSyncTest()
{
var tasks = new List<Task>();
for (int i = 0; i < 5; i++)
{
tasks.Add(this.RunTask());
}
await Task.WhenAll(tasks);
}
//This is just an example of some synchronous task that I want to run in parallel.
//Is there something I can do in this method that makes the async keyword work? I.e. this would run asynchronously when called from ThisWillRunSyncTest
public async Task RunTask()
{
Thread.Sleep(1000);
}
}
Async/await helps you write synchronous-looking JavaScript code that works asynchronously. Await is in an async function to ensure that all promises that are returned in the function are synchronized. With async/await, there's no use of callbacks.
Solution A If you have a simple asynchronous method that doesn't need to synchronize back to its context, then you can use Task. WaitAndUnwrapException : var task = MyAsyncMethod(); var result = task. WaitAndUnwrapException();
The call to the async method starts an asynchronous task. However, because no Await operator is applied, the program continues without waiting for the task to complete. In most cases, that behavior isn't expected.
GetAwaiter. GetResult pattern to actually convert async to sync. But if you do that byitself in WPF or another SynchronizationContext bound to a particular thread you deadlock. This code avoids deadlock by transferring the async operation to the threadpool which is not synchronized to a particular thread.
As a general rule, if you have parallel work to do, you should be using Parallel
or parallel LINQ.
There are times when it's convenient to treat CPU-bound work as though it were asynchronous (i.e., running it on a background thread). This is what Task.Run
is for (avoid using StartNew
, as I describe on my blog).
Synchronous methods should have synchronous method signatures:
public void RunTask()
{
Thread.Sleep(1000);
}
They should only be wrapped in Task.Run
if the calling code requires it (i.e., it is part of a UI component such as a view model):
var tasks = new List<Task>();
for (int i = 0; i < 5; i++)
{
tasks.Add(Task.Run(() => this.RunTask()));
}
await Task.WhenAll(tasks);
The principle here is that Task.Run
should be used in the invocation, not the implementation; I go into more details on my blog.
Note that if you have any real complexity, you should be using Parallel
or parallel LINQ instead of a collection of Task.Run
tasks. Task.Run
is fine for small stuff but it doesn't have all the smarts the parallel types do. So, if this is part of a library (and not necessarily running on a UI thread), then I'd recommend using Parallel
:
Parallel.For(0, 5, _ => this.RunTask());
As a final side note, asynchronous unit test methods should be async Task
, not async void
. NUnit v3 has already removed support for async void
unit test methods.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With