Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to deal with synchronous method/task using async/await

Tags:

c#

async-await

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);
    }
}
like image 356
cgotberg Avatar asked Oct 23 '14 17:10

cgotberg


People also ask

Can you use await in a synchronous function?

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.

Can we call async method in sync method?

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();

What happens if we execute an asynchronous method but do not await it?

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.

Which is the recommended way to wait for an async method to complete?

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.


1 Answers

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.

like image 65
Stephen Cleary Avatar answered Oct 20 '22 08:10

Stephen Cleary