Write your own async method

18,791

Solution 1

Actual Answer

You do that using TaskCompletionSource, which has a Promise Task that doesn't execute any code and only:

"Represents the producer side of a Task unbound to a delegate, providing access to the consumer side through the Task property."

You return that task to the caller when you start the asynchronous operation and you set the result (or exception/cancellation) when you end it. Making sure the operation is really asynchronous is on you.

Here is a good example of this kind of root of all async method in Stephen Toub's AsyncManualResetEvent implementation:

class AsyncManualResetEvent 
{ 
    private volatile TaskCompletionSource<bool> _tcs = new TaskCompletionSource<bool>();

    public Task WaitAsync() { return _tcs.Task; } 
    public void Set() { _tcs.TrySetResult(true); } 
    public void Reset() 
    { 
        while (true) 
        { 
            var tcs = _tcs; 
            if (!tcs.Task.IsCompleted || 
                Interlocked.CompareExchange(ref _tcs, new TaskCompletionSource<bool>(), tcs) == tcs) 
                return; 
        } 
    } 
}

Background

There are basically two reasons to use async-await:

  1. Improved scalability: When you have I/O intensive work (or other inherently asynchronous operations), you can call it asynchronously and so you release the calling thread and it's capable of doing other work in the mean time.
  2. Offloading: When you have CPU intensive work, you can call it asynchronously, which moves the work off of one thread to another (mostly used for GUI threads).

So most of the .Net framework's asynchronous calls support async out of the box and for offloading you use Task.Run (as in your example). The only case where you actually need to implement async yourself is when you create a new asynchronous call (I/O or async synchronization constructs for example).

These cases are extremely rare, which is why you mostly find answers that

"Only explains it with already existing methods and just using async/await pattern"


You can go deeper in The Nature of TaskCompletionSource

Solution 2

Would be nice if you could explain it to me: How to write simple async method?

First, we need to understand what an async method means. When one exposes an async method to the end user consuming the async method, you're telling him: "Listen, this method will return to you quickly with a promise of completing sometime in the near future". That is what you're guaranteeing to your users.

Now, we need to understand how Task makes this "promise" possible. As you ask in your question, why simply adding a Task.Run inside my method makes it valid to be awaited using the await keyword?

A Task implements the GetAwaiter pattern, meaning it returns an object called an awaiter (Its actually called TaskAwaiter). The TaskAwaiter object implements either INotifyCompletion or ICriticalNotifyCompletion interfaces, exposing a OnCompleted method.

All these goodies are in turn used by the compiler once the await keyword is used. The compiler will make sure that at design time, your object implements GetAwaiter, and in turn use that to compile the code into a state machine, which will enable your program to yield control back to the caller once awaited, and resume when that work is completed.

Now, there are some guidelines to follow. A true async method doesn't use extra threads behind the scenes to do its job (Stephan Cleary explains this wonderfully in There Is No Thread), meaning that exposing a method which uses Task.Run inside is a bit misleading to the consumers of your api, because they will assume no extra threading involved in your task. What you should do is expose your API synchronously, and let the user offload it using Task.Run himself, controlling the flow of execution.

async methods are primarily used for I/O Bound operations, since these naturally don't need any threads to be consumed while the IO operation is executing, and that is why we see them alot in classes responsible for doing IO operations, such as hard drive calls, network calls, etc.

I suggest reading the Parallel PFX teams article Should I expose asynchronous wrappers for synchronous methods? which talks exactly about what you're trying to do and why it isn't recommended.

Solution 3

TL;DR:

Task.Run() is what you want, but be careful about hiding it in your library.

I could be wrong, but you might be looking for guidance on getting CPU-Bound code to run asynchronously [by Stephen Cleary]. I've had trouble finding this too, and I think the reason it's so difficult is that's kinda not what you're supposed to do for a library - kinda...

Article

The linked article is a good read (5-15 minutes, depending) that goes into a decent amount of detail about the hows and whys of using Task.Run() as part of an API vs using it to not block a UI thread - and distinguishes between two "types" of long-running process that people like to run asynchronously:

  • CPU-bound process (one that is crunching / actually working and needs a separate thread to do its work)
  • Truly asynchronous operation (sometimes called IO-bound - one that is doing a few things here and there with a bunch of waiting time in between actions and would be better off not hogging a thread while it's sitting there doing nothing).

The article touches on the use of API functions in various contexts, and explains whether the associated architectures "prefer" sync or async methods, and how an API with sync and async method signatures "looks" to a developer.

Answer

The last section "OK, enough about the wrong solutions? How do we fix this the right way???" goes into what I think you're asking about, ending with this:

Conclusion: do not use Task.Run in the implementation of the method; instead, use Task.Run to call the method.

Basically, Task.Run() 'hogs' a thread, and is thus the thing to use for CPU-bound work, but it comes down to where it's used. When you're trying to do something that requires a lot of work and you don't want to block the UI Thread, use Task.Run() to run the hard-work function directly (that is, in the event handler or your UI based code):

class MyService
{
  public int CalculateMandelbrot()
  {
    // Tons of work to do in here!
    for (int i = 0; i != 10000000; ++i)
      ;
    return 42;
  }
}

...

private async void MyButton_Click(object sender, EventArgs e)
{
  await Task.Run(() => myService.CalculateMandelbrot());
}

But... don't hide your Task.Run() in an API function suffixed -Async if it is a CPU-bound function, as basically every -Async function is truly asynchronous, and not CPU-bound.

// Warning: bad code!
class MyService
{
  public int CalculateMandelbrot()
  {
    // Tons of work to do in here!
    for (int i = 0; i != 10000000; ++i)
      ;
    return 42;
  }

  public Task<int> CalculateMandelbrotAsync()
  {
    return Task.Run(() => CalculateMandelbrot());
  }
}

In other words, don't call a CPU-bound function -Async, because users will assume it is IO-bound - just call it asynchronously using Task.Run(), and let other users do the same when they feel it's appropriate. Alternately, name it something else that makes sense to you (maybe BeginAsyncIndependentWork() or StartIndependentWorkTask()).

Share:
18,791

Related videos on Youtube

Rand Random
Author by

Rand Random

Updated on June 06, 2022

Comments

  • Rand Random
    Rand Random almost 2 years

    I would like to know how to write your own async methods the "correct" way.

    I have seen many many posts explaining the async/await pattern like this:

    http://msdn.microsoft.com/en-us/library/hh191443.aspx

    // Three things to note in the signature: 
    //  - The method has an async modifier.  
    //  - The return type is Task or Task<T>. (See "Return Types" section.)
    //    Here, it is Task<int> because the return statement returns an integer. 
    //  - The method name ends in "Async."
    async Task<int> AccessTheWebAsync()
    { 
        // You need to add a reference to System.Net.Http to declare client.
        HttpClient client = new HttpClient();
    
        // GetStringAsync returns a Task<string>. That means that when you await the 
        // task you'll get a string (urlContents).
        Task<string> getStringTask = client.GetStringAsync("http://msdn.microsoft.com");
    
        // You can do work here that doesn't rely on the string from GetStringAsync.
        DoIndependentWork();
    
        // The await operator suspends AccessTheWebAsync. 
        //  - AccessTheWebAsync can't continue until getStringTask is complete. 
        //  - Meanwhile, control returns to the caller of AccessTheWebAsync. 
        //  - Control resumes here when getStringTask is complete.  
        //  - The await operator then retrieves the string result from getStringTask. 
        string urlContents = await getStringTask;
    
        // The return statement specifies an integer result. 
        // Any methods that are awaiting AccessTheWebAsync retrieve the length value. 
        return urlContents.Length;
    }
    
    private void DoIndependentWork()
    {
        resultsTextBox.Text += "Working........\r\n";
    }
    

    This works great for any .NET Method that already implements this functionality like

    • System.IO opertions
    • DataBase opertions
    • Network related operations (downloading, uploading...)

    But what if I want to write my own method that takes quite some time to complete where there just is no Method I can use and the heavy load is in the DoIndependentWork method of the above example?

    In this method I could do:

    • String manipulations
    • Calculations
    • Handling my own objects
    • Aggregating, comparing, filtering, grouping, handling stuff
    • List operations, adding, removing, coping

    Again I have stumbled across many many posts where people just do the following (again taking the above example):

    async Task<int> AccessTheWebAsync()
    { 
        HttpClient client = new HttpClient();
    
        Task<string> getStringTask = client.GetStringAsync("http://msdn.microsoft.com");
    
        await DoIndependentWork();
    
        string urlContents = await getStringTask;
    
        return urlContents.Length;
    }
    
    private Task DoIndependentWork()
    {
        return Task.Run(() => {
    
            //String manipulations
            //Calculations
            //Handling my own objects
            //Aggregating, comparing, filtering, grouping, handling stuff
            //List operations, adding, removing, coping
        });
    }
    

    You may notice that the changes are that DoIndependentWork now returns a Task and in the AccessTheWebAsync task the method got an await.

    The heavy load operations are now capsulated inside a Task.Run(), is this all it takes? If that's all it takes is the only thing I need to do to provide async Method for every single method in my library the following:

    public class FooMagic
    {
        public void DoSomeMagic()
        {
            //Do some synchron magic...
        }
    
        public Task DoSomeMagicAsync()
        {
            //Do some async magic... ?!?
            return Task.Run(() => { DoSomeMagic(); });
        }
    }
    

    Would be nice if you could explain it to me since even a high voted question like this: How to write simple async method? only explains it with already existing methods and just using asyn/await pattern like this comment of the mentioned question brings it to the point: How to write simple async method?

    • Kimmax
      Kimmax almost 10 years
      I may be a bit off topic, but will resultsTextBox.Text += "Working........\r\n"; even work? This runs in a No-GUI thread, so you would need an invoke, right? Thanks.
    • Code Jockey
      Code Jockey over 8 years
      @Kimmax not sure - still working through this, but I'm currently somewhat under the impression that an async signature adds marshalling in behind the scenes, or maybe that's only when you await something?
    • Kimmax
      Kimmax over 8 years
      @CodeJockey thanks for rising this comment from the dead :P Meanwhile I do understand more and know that using something like Progress.Report and data-binding completely solves this.
    • CrookedBadge
      CrookedBadge about 7 years
      Great question, I have been wondering the exact same thing and not found any good examples. The top answer helped to clarify some thing, but just like you I would like more from that answer.
  • Rand Random
    Rand Random over 9 years
    I am not really satisfied with the answer, since I am missing the "how you write it" from the answer I only get "How NOT to write it", maybe you could supply a little code sample of how you would write a simple async method? I am more looking into offloading instead of scalability since my project is a Entity Framework project and EF it self doesn't support multi async operation with 1 context.
  • Tim Barrass
    Tim Barrass over 8 years
    I think it's clarifying your question, as the definition of async can be vague. Yuval's saying that in a loose sense you have written an async method. However, in a strict sense it's not a true async method, as you're handing off work to a Task (or thread) underneath; in a strict async method there is no thread -- the OS will have handed responsibility off to some bit of hardware.
  • Jimi
    Jimi about 8 years
    A great MSDN article on the subject.
  • John
    John over 6 years
    @YuvalItzchakov Is there a code sample about how to write a true async method( no extra threads and not await other async method ) ?