2012-02-02

Async and Await

Most people have already heard about the new "async" and "await" functionality coming in Visual Studio 11. This is Yet Another Introductory Post.

First, the punchline: Async will fundamentally change the way most code is written.

Yup, I believe async/await will have a bigger impact than LINQ. Understanding async will be a basic necessity just a few short years from now.

Introducing the Keywords

Let's dive right in. I'll use some concepts that I'll expound on later on - just hold on for this first part.

Asynchronous methods look something like this:

public async Task DoSomethingAsync()
{
  // In the Real World, we would actually do something...
  // For this example, we're just going to (asynchronously) wait 100ms.
  await Task.Delay(100);
}

The "async" keyword enables the "await" keyword in that method. That's all the async keyword does! It does not run this method on a thread pool thread, or do any other kind of magic. The async keyword only enables the await keyword.

The beginning of an async method is executed just like any other method. That is, it runs synchronously until it hits an "await" (or throws an exception).

The "await" keyword is where things can get asynchronous. Await is like a unary operator: it takes a single argument, an awaitable (an "awaitable" is an asynchronous operation). Await examines that awaitable to see if it has already completed; if the awaitable has already completed, then the method just continues running (synchronously, just like a regular method).

If "await" sees that the awaitable has not completed, then it acts asynchronously. It tells the awaitable to run the remainder of the method when it completes, and then returns from the async method.

Later on, when the awaitable completes, it will execute the remainder of the async method. If you're awaiting a built-in awaitable (such as a task), then the remainder of the async method will execute on a "context" that was captured before the "await" returned.

I like to think of "await" as an "asynchronous wait". That is to say, the async method pauses until the awaitable is complete (so it waits), but the actual thread is not blocked (so it's asynchronous).

Awaitables

As I mentioned, "await" takes a single argument - an "awaitable" - which is an asynchronous operation. There are two awaitable types already common in the .NET framework: Task<T> and Task.

There are also other awaitable types: special methods such as "Task.Yield" return awaitables that are not Tasks, and the WinRT runtime (coming in Windows 8) has an unmanaged awaitable type. You can also create your own awaitable (usually for performance reasons), or use extension methods to make a non-awaitable type awaitable.

That's all I'm going to say about making your own awaitables. I've only had to write a couple of awaitables in the entire time I've used async/await. If you want to know more about writing your own awaitables, see the Parallel Team Blog or Jon Skeet's Blog.

One important point about awaitables is this: it is the type that is awaitable, not the method returning the type. In other words, you can await the result of an async method that returns Task ... because the method returns Task, not because it's async. So you can also await the result of a non-async method that returns Task:

public async Task NewStuffAsync()
{
  // Use await and have fun with the new stuff.
  await ...
}

public Task MyOldTaskParallelLibraryCode()
{
  // Note that this is not an async method, so we can't use await in here.
  ...
}

public async Task ComposeAsync()
{
  // We can await Tasks, regardless of where they come from.
  await NewStuffAsync();
  await MyOldTaskParallelLibraryCode();
}
Tip: If you have a very simple asynchronous method, you may be able to write it without using the await keyword (e.g., using Task.FromResult). If you can write it without await, then you should write it without await, and remove the async keyword from the method. A non-async method returning Task.FromResult is more efficient than an async method returning a value.

Return Types

Async methods can return Task<T>, Task, or void. In almost all cases, you want to return Task<T> or Task, and return void only when you have to.

Why return Task<T> or Task? Because they're awaitable, and void is not. So if you have an async method returning Task<T> or Task, then you can pass the result to await. With a void method, you don't have anything to pass to await.

You have to return void when you have async event handlers.

You can also use async void for other "top-level" kinds of actions - e.g., a single "static async void MainAsync()" for Console programs. However, this use of async void has its own problem; see Async Console Programs. The primary use case for async void methods is event handlers.

Returning Values

Async methods returning Task or void do not have a return value. Async methods returning Task<T> must return a value of type T:

public async Task<int> CalculateAnswer()
{
  await Task.Delay(100); // (Probably should be longer...)

  // Return a type of "int", not "Task<int>"
  return 42;
}

This is a bit odd to get used to, but there are good reasons behind this design.

Context

In the overview, I mentioned that when you await a built-in awaitable, then the awaitable will capture the current "context" and later apply it to the remainder of the async method. What exactly is that "context"?

Simple answer:

  1. If you're on a UI thread, then it's a UI context.
  2. If you're responding to an ASP.NET request, then it's an ASP.NET request context.
  3. Otherwise, it's usually a thread pool context.

Complex answer:

  1. If SynchronizationContext.Current is not null, then it's the current SynchronizationContext. (UI and ASP.NET request contexts are SynchronizationContext contexts).
  2. Otherwise, it's the current TaskScheduler (TaskScheduler.Default is the thread pool context).

What does this mean in the real world? For one thing, capturing (and restoring) the UI/ASP.NET context is done transparently:

// WinForms example (it works exactly the same for WPF).
private async void DownloadFileButton_Click(object sender, EventArgs e)
{
  // Since we asynchronously wait, the UI thread is not blocked by the file download.
  await DownloadFileAsync(fileNameTextBox.Text);

  // Since we resume on the UI context, we can directly access UI elements.
  resultTextBox.Text = "File downloaded!";
}

// ASP.NET example
protected async void MyButton_Click(object sender, EventArgs e)
{
  // Since we asynchronously wait, the ASP.NET thread is not blocked by the file download.
  // This allows the thread to handle other requests while we're waiting.
  await DownloadFileAsync(...);

  // Since we resume on the ASP.NET context, we can access the current request.
  // We may actually be on another *thread*, but we have the same ASP.NET request context.
  Response.Write("File downloaded!");
}

This is great for event handlers, but it turns out to not be what you want for most other code (which is, really, most of the async code you'll be writing).

Avoiding Context

Most of the time, you don't need to sync back to the "main" context. Most async methods will be designed with composition in mind: they await other operations, and each one represents an asynchronous operation itself (which can be composed by others). In this case, you want to tell the awaiter to not capture the current context by calling ConfigureAwait and passing false, e.g.:

private async Task DownloadFileAsync(string fileName)
{
  // Use HttpClient or whatever to download the file contents.
  var fileContents = await DownloadFileContentsAsync(fileName).ConfigureAwait(false);

  // Note that because of the ConfigureAwait(false), we are not on the original context here.
  // Instead, we're running on the thread pool.

  // Write the file contents out to a disk file.
  await WriteToDiskAsync(fileName, fileContents).ConfigureAwait(false);

  // The second call to ConfigureAwait(false) is not *required*, but it is Good Practice.
}

// WinForms example (it works exactly the same for WPF).
private async void DownloadFileButton_Click(object sender, EventArgs e)
{
  // Since we asynchronously wait, the UI thread is not blocked by the file download.
  await DownloadFileAsync(fileNameTextBox.Text);

  // Since we resume on the UI context, we can directly access UI elements.
  resultTextBox.Text = "File downloaded!";
}

The important thing to note with this example is that each "level" of async method calls has its own context. DownloadFileButton_Click started in the UI context, and called DownloadFileAsync. DownloadFileAsync also started in the UI context, but then stepped out of its context by calling ConfigureAwait(false). The rest of DownloadFileAsync runs in the thread pool context. However, when DownloadFileAsync completes and DownloadFileButton_Click resumes, it does resume in the UI context.

A good rule of thumb is to use ConfigureAwait(false) unless you know you do need the context.

Async Composition

So far, we've only considered serial composition: an async method waits for one operation at a time. It's also possible to start several operations and await for one (or all) of them to complete. You can do this by starting the operations but not awaiting them until later:

public async Task DoOperationsConcurrentlyAsync()
{
  Task[] tasks = new Task[3];
  tasks[0] = DoOperation0Async();
  tasks[1] = DoOperation1Async();
  tasks[2] = DoOperation2Async();

  // At this point, all three tasks are running at the same time.

  // Now, we await them all.
  await Task.WhenAll(tasks);
}

public async Task<int> GetFirstToRespondAsync()
{
  // Call two web services; take the first response.
  Task<int>[] tasks = new[] { WebService1Async(), WebService2Async() };

  // Await for the first one to respond.
  Task<int> firstTask = await Task.WhenAny(tasks);

  // Return the result.
  return await firstTask;
}

By using concurrent composition (Task.WhenAll or Task.WhenAny), you can perform simple concurrent operations. You can also use these methods along with Task.Run to do simple parallel computation. However, this is not a substitute for the Task Parallel Library - any advanced CPU-intensive parallel operations should be done with the TPL.

Guidelines

Read the Task-based Asynchronous Pattern (TAP) document. It is extremely well-written, and includes guidance on API design and the proper use of async/await (including cancellation and progress reporting).

There are many new await-friendly techniques that should be used instead of the old blocking techniques. If you have any of these Old examples in your new async code, you're Doing It Wrong(TM):

OldNewDescription
task.Waitawait taskWait/await for a task to complete
task.Resultawait taskGet the result of a completed task
Task.WaitAnyawait Task.WhenAnyWait/await for one of a collection of tasks to complete
Task.WaitAllawait Task.WhenAllWait/await for every one of a collection of tasks to complete
Thread.Sleepawait Task.DelayWait/await for a period of time
Task constructorTask.Run or TaskFactory.StartNewCreate a code-based task

Next Steps

I have published an MSDN article Best Practices in Asynchronous Programming, which further explains the "avoid async void", "async all the way" and "configure context" guidelines.

The official MSDN documentation is quite good; they include an online version of the Task-based Asynchronous Pattern document which is excellent, covering the designs of asynchronous methods.

The async team has published an async/await FAQ that is a great place to continue learning about async. They have pointers to the best blog posts and videos on there. Also, pretty much any blog post by Stephen Toub is instructive!

Of course, another resource is my own blog. This blog post is the beginning of a series of posts on async.

50 comments:

  1. VERY GOOD ! THANKS !
    hdolder
    [www.hdolder.com/CutBSK6fN.htm]

    ReplyDelete
  2. Many thanks for this excellent blog!
    I'm only questioning your statement [Most of the time, you don't need to sync back to the "main" context]. Don't you think most programmers will forget about locks needed when accessing shared data from several threads? This is especially dangerous, when programming asynchronous operations in a 'synchronous style'.

    ReplyDelete
    Replies
    1. Each async method is processed serially. This is true even if ConfigureAwait(false) is used; the async method will continue on a thread pool thread, but each part of the async method is still dependent on the previous part of the async method.

      The only case where locks would be needed is when you actually have multiple threads running at the same time (e.g., starting several Task.Run tasks and then awaiting Task.WhenAll). In that case, Stephen Toub has done some very interesting work with await-friendly synchronization primitives (on the MSDN parallel team blog).

      Delete
    2. Hi Stephen,
      Thanks for your answer!
      To stress my point I made a small console application.
      One run generates about 5-15 race conditions on my laptop.
      My recommendation is to always use a SychronizationContext unless you really have to optimize the performance.
      / Stefan.

      static void Main( string [] args ){
      for( int i=0; i < 100000; i++ ){
      TestThreadSafety();
      }
      Console.WriteLine( "done." );
      Console.Read();
      }

      static int m_counter;

      static async void TestThreadSafety(){
      await Task.Delay( 10 );

      int n = m_counter + 1;
      m_counter++;
      if( n != m_counter ){
      Console.WriteLine( "Error: counter=" + m_counter + ", n=" + n );
      }
      }

      Delete
    3. Stefan: I'm not saying that async code doesn't *ever* need locks.

      Rather, I'm saying that async code is naturally functional (side-effect free). Async code really doesn't mix well with state at all (whether object state or global state, such as is in your example). Usually, the only shared variables will be local variables, which will not require locks.

      Delete
    4. Yes, its really about some higher level design that has to be defined before deciding such points. I have an actor based model in mind while your's is a functional programming model. I will have a deeper look at functional programming. So far I have no clue on how to model the 'reality' when I'm not allowed to change the models state. I actually could generate new versions of the model for every modification - but then I would have several threads working on different versions of the model - how this could work out is currently beyond my imagination...
      Thanks for the discussion.
      /Stefan

      Delete
  3. Absolutely Amazingly described!!! I read so many places but this is the best.

    ReplyDelete
  4. amazing.. great !! easy to understand..!!

    ReplyDelete
  5. Thanks for the introduction!

    Did your forget ConfigureAwait in the WinForms example code!

    ReplyDelete
    Replies
    1. No, the DownloadFileButton_Click does not call ConfigureAwait on purpose. It has to resume on the UI thread because it modifies a UI object after its await.

      This pattern is rather common: library async methods do use ConfigureAwait, and UI async methods do not use ConfigureAwait.

      Delete
  6. Thanks for the gr8 article. I got stuck at this statement,
    "By using parallel composition (Task.WhenAll or Task.WhenAny), you can perform simple parallel operations. However, this is not a substitute for the Task Parallel Library - any advanced CPU-intensive parallel operations should probably be done with the TPL." By using Tasks we are already using TPL, so how it is different if we use - await Task.WhenAll(tasks). Please elaborate.

    ReplyDelete
    Replies
    1. Technically, the Task type is a part of the Task Parallel Library. When the async team needed a "future" object, they chose to (re)use Task rather than create one themselves.

      So, when I'm talking about using TPL for CPU-intensive parallel operations, I mean the Parallel class, AsParallel, and other TPL constructs that are not normally used in async code.

      Delete
  7. "It does not run this method on a thread pool thread" .. ah thanks, people should say this more often. I had associated tasks with multithreading too much...which was very confusing.

    ReplyDelete
  8. I'm a little confused though, if a method returns a task but the methods definition is on the UI thread and its execution does not run in a new thread then how can that returned task be awaited on without blocking the UI thread?

    Don't all async defined methods run from a new thread in the pool?

    ReplyDelete
    Replies
    1. No, async methods do not run from a new thread in the pool.

      When you await an incomplete task, you are suspending the current method. After the task completes, the method resumes execution.

      Delete
    2. So if you await a task returned by a method that has no async implementations defined within it, the task being awaited on will lock up the thread it was returned from e.g UI thread?

      Cheers.

      Delete
    3. If you await an incomplete task, then the current method is suspended.

      If you call a method that does not await, then that method runs synchronously. This is true regardless of the return type of the method or whether or not it is marked "async".

      Delete
  9. It is crazy how that well that bible verse in the header (55:11) lines up with async/await. I'm not religious at all, but that is pretty cool. I'll be sure to say that to my code with the best booming voice I can come up with.

    ReplyDelete
  10. Great job at a very very easy to understand article!

    ReplyDelete
  11. Thanks for your well written explanation. But I am curious whether a task can be awaited by two different tasks. In case of a program in which two tasks need to execute after a task T, can they both await T? How will T's continuation will be set in such a case?
    Thanks in advance.

    ReplyDelete
    Replies
    1. Yes, a single task can be await'ed by any number of async methods. "Await" will first check the task to see if it has completed; if it is already completed, then there is no continuation at all. If the task is not already completed, then it will *add* a continuation to the task. Each task can have any number of continuations.

      One common use case for this is (shared) resources that require asynchronous initialization. You can declare the resource as such:

      class MyClass
      {
      private readonly Task resource;
      public MyClass()
      {
      resource = LoadMyResourceAsync(); // Note: no "await"
      }
      }

      and then multiple methods can await that resource:

      public async Task MethodA()
      {
      var res = await resource;
      ...
      }

      public async Task MethodB()
      {
      var res = await resource;
      ...
      }

      Keep in mind that when the resource is done loading, it will continue all async methods that have already await'ed it, and any new async methods that await it will continue running synchronously (since it is already loaded, there's nothing to wait for).

      If you want to *lazy*-load an asynchronous resource (very similar to this but delaying the loading of the resource until it's actually needed), I have another blog post for that: http://blog.stephencleary.com/2012/08/asynchronous-lazy-initialization.html

      Delete
    2. Thank you, it is really helpful.

      Delete
  12. Excellent article. One of the best in internet which explains the purpose of async and await keywords!

    ReplyDelete
  13. Hi Stephen,

    This is the kind of explanation I had been looking for. But still, I can't figure out how to get things geared up. Please take a look at my simple code (VB.NET)

    Public Class Form1

    Private Async Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    TextBox1.Text += vbCrLf + "No connection yet"
    TextBox1.Text += vbCrLf + Await DoAsync()
    TextBox1.Text += vbCrLf + "Done"
    End Sub

    Private Async Function DoAsync() As Task(Of String)
    Return Await DoJobAsync()
    End Function

    Private Async Function DoJobAsync() As Task(Of String)
    Threading.Thread.Sleep(2000)
    Return "Connection made"
    End Function

    End Class

    This works allright, but on the UI everything waits for 2 seconds. When I use a bigger amount of milliseconds, then everything waits that much longer. That is not the behaviour I would expect from asynchronism. But where do I miss the point?

    Hope to hear from you!
    Best regards,
    Peter

    ReplyDelete
    Replies
    1. Don't ignore the compiler warnings. In this case, the compiler notifies you that DoJobAsync does not contain any await statements and will therefore run synchronously.

      As I said in this blog post, when an async method starts, it runs synchronously until it hits its first await. Since there is no await in DoJobAsync, then the entire method runs synchronously.

      The problem is in Thread.Sleep, which blocks the current (UI) thread. You want to avoid long blocking like this in async code. To fix this, you can replace Thread.Sleep with Await Task.Delay, which will asynchronously wait for the specified amount of time.

      Delete
  14. Thanks a lot! You really have a talent to explain difficult topics in an easy-to-understand manner.

    ReplyDelete
  15. This comment has been removed by the author.

    ReplyDelete
  16. Great article.

    Just wanted to clarify something to make sure I understand - I haven't used ConfigureAwait before, so I want to make sure I don't screw this up. Suppose I have MVC (regular or WebAPI) as my entry point, which calls code in my business layer, which in turn calls out to another API somewhere, and I want that all asynchronous. Assuming I want my controller to never lose its MVC context, I would be safe using ConfigureAwait(false) everywhere except the very first call from the controller to my business layer, right? For example:

    // In MVC
    public class MyController : Controller {
      public async Task<ActionResult> Go() {
        Foo something = await _businessLayer.GetSomething();
        return View(something);
      }
    }

    // In my business library - a separate library which knows nothing about MVC or ASP.NET
    public class BusinessLayer {
      public async Task<Foo> GetSomething() {
        return await _myApiWrapper.GetSomething<Foo>(someUrl).ConfigureAwait(false);
      }
    }

    public class ApiWrapper {
      public async Task<T> GetSomething(string url) {
        var httpClient = new HttpClient();
        var httpResult = await httpClient.GetAsync(url).ConfigureAwait(false);
        var content = await httpResult.Content.ReadAsAsync<T>().ConfigureAwait(false);
        return content;
      }
    }

    ReplyDelete
    Replies
    1. Yes, that is both correct and the *proper* way to do it. This works because every await captures its *own* context. So the ApiWrapper.GetSomething and BusinessLayer.GetSomething will complete on a thread pool thread (discarding the context), but MyController.Go will complete on the original ASP.NET context.

      (There are various optimizations in place so that what actually happens is that when the last await completes in ApiWrapper.GetSomething, it will run on a thread pool thread which also finishes BusinessLayer.GetSomething and then enters the ASP.NET request context and finishes MyController.Go all on the same thread - but this is just an optimization detail).

      I have an MSDN article online (http://msdn.microsoft.com/en-us/magazine/jj991977.aspx) that goes into more detail around the best practices, but to summarize the guideline is to use ConfigureAwait(false) unless you know that you *do* need the context. Your example code is exactly correct.

      Delete
    2. Awesome, thanks. I appreciate the clear explanation.

      Delete
  17. Clear definition of the keywords.
    Clearest I have found
    thus far drowning on the internet.
    Got this bookmarked
    for reference now. Thanks!

    ReplyDelete
  18. Hi Steve! Just wanted to thank you for a fantastically lucid explanation of ASYNC/AWAIT. I found MSDN docs not so useful in this case.

    ReplyDelete
  19. Thx for the tips, but as curiosity, and I mean no disrespect, how come someone intelligent, a programmer! :P, believes in a personal god!?!? I mean, if it were an Espinoza like god (which might have some meaning and some background maybe...) I'd understand it, but christian!? come on, your wife must be really beautiful to convince u on the matter :D (joking!)

    ReplyDelete
    Replies
    1. If I read your question correctly, your current understanding is that intelligent people do not believe in a personal god. The Bible takes a more pragmatic perspective: it says there are *not many* intelligent (or powerful, or rich) people who choose to believe, but that with God all things are possible (I Cor 1:26; Matt 19:24-26). Personally, I suspect it is because salvation requires a fair amount of humility; in essence, before one can be saved (by a Savior, not themselves), they must first see themselves as (hopelessly) lost.

      I believe in Jesus Christ, that He is the Son of God, that He lived a sinless life and then gave that life up when He was crucified, and that He rose from the grave, thus giving us the hope of Heaven some day. But it's easy to talk about Jesus just as an impersonal "fire insurance". In reality, He is much more. I have a personal relationship with Jesus Christ, based on my belief in Him as a personal God.

      My relationship with Jesus began in my teen years, and I can say with absolute certainty that without His direct influence in my life, I would not be alive today. BTW I met my wife in college, long after I dedicated my life to God. :)

      I encourage you to reconsider your belief that there is no god. Christianity is - at its core - entirely based on faith. There is no proof. If you want to look into it a bit yourself, I'd recommend going straight to the source: get a Bible (I recommend the KJV, both for its accuracy and language, but it can be harder to understand), and read the books of John and Romans. They tell the story of Jesus and how Christianity started. Before you read them, ask God to reveal Himself to you (*if* He's there).

      Best regards,
      -Steve

      Delete
    2. Hey Stephen :) Thx for taking the time to answer! But I can't help not to reach with another remark: for a guy who understands a lot about async, multithreaded and concurrent programming, you seam to not understand (or maybe you do, but ignore!?) the current scientific view of the world...I mean, what about our astrophysical origins, what about muons gluons and quorks, dark matter and dark energy, string theory and membranes, the billions of galaxies with trillions of planets which, some of them, are very likely to host some kind of biological and/or intelligent life?! What I want to say, is that our religious beliefs are kind of anecdotal, when regarded through the grand scheme of things - a small part of them mentioned previously. So instead of questioning ourselves about our nature, origins, purpose and fundamental facts backed by some scientific understanding, you are saying that we should choose to believe in things that are a couple of thousand years old, written by people with little or no scientific understanding whatsoever (yes, I know that some people like Isaac Newton believed in a personal God :/, but Einstein for instance, was inclined to believe in some Espinoza like God! Both were wrong in my opinion, there is no evidence for anything like that)...Bottom line, I think you should reconsider!

      Delete
    3. Actually, I enjoy reading about modern scientific theories such as string theory and quantum mechanics. Though I cannot pretend to understand it all. :)

      But I'm also somewhat aware of how often scientific theories change. For example, it is well-known that the Big Bang Theory - as currently formulated - is wrong. There are a few competing replacement theories, but they all depend on some unprovable assumptions. Most of what you mention, from quarks to dark matter, are all hypothoses and have not been proven. Given that scientific progress is usually made by *overturning* the current theories, I read about modern theories such as string theory with interest but not firm belief.

      In particular, I'm not a fan of dark matter / dark energy. To me that seems more like twisting the data to fit the hypothesis instead of fixing the hypothesis to match the data.

      But back to the point - belief in a personal God. Even if, e.g., dark matter exists as we currently theorize it, I'm not sure why dark matter would preclude the existence of God?

      As far as the universe goes, I consider nature one of the strongest arguments in *favor* of an intelligent creator. Mankind did not create quarks or dark energy; we are only discovering them.

      P.S. As a Christian, I believe that God wrote the Bible, so the scientific knowledge of the human authors is immaterial. Also, I could be wrong, but I do not believe Einstein believed in a God, even as a Deist.

      Delete
    4. Oh come on, you seem a smart an reasonable person :) I don't understand the things I talked about either, but I tend to assume as true, opinions expressed by current or RIP (like richard feynman, Nobel prize in physics) scientists, like michio kaku & co, because they make more sense then the advocates of religious beliefs...Anyways, is good to keep an opened mind and of course respect above all...and of course, your blog seems very nice and useful :) Cheers!

      Delete
    5. Ha, you guys are a trip. Pretty fun to read. There's a good probability we are just "living" our lives in an advanced computer simulation, either created by our future selves or a more advanced being (a.k.a. God). Thus, learning to program is simply giving ourselves the ability to customize our existence. Cheers.

      Delete
    6. @anemet: Mate - and I say this as a computer programmer and an atheist myself - your posts suck. Your opinions are meaningless because you don't understand the (pretty major) assumptions you've based them on - in fact, you don't even seem to realise you're making assumptions in the first place. If you do believe your (somewhat ironic) comment that it's "good to keep an open mind", then put down the Richard Dawkins books for five minutes and enrol on a Philosophy 101 course. None of us truly knows where the whole of existence came from - or even whether it did.

      For various reasons, I don't believe we ever can know, and I like to think of that as the Universe's way of making sure things will never get boring :)

      Delete
  20. Thanks, took me a while to find a clear and concise explanation of this syntax.

    ReplyDelete
  21. Please look at the point where you say " // At this point, all three tasks are running in parallel."... since it is still running in the same thread then it would take the same total time as if you wrote DoOperation0(); DoOperation1(); DoOperation2();
    Is that correct?

    ReplyDelete
    Replies
    1. My use of "parallel" there is confusing. If the operations are CPU-bound and there is a single-thread context, then yes, it would take the same total time.

      However, if the operations are naturally asynchronous, then they can all run concurrently on the same thread.

      Delete
    2. Thanks, I understand this part now. So for loading from HDD operations (loading images and extracting thumbnails), does it make sense to use Task Factory or use just task/await?

      Delete
    3. For loading data from disk, use async. If you're doing image manipulation, ideally you would use parallel or background tasks. However, the built-in image manipulation (BitmapImage etc) is mostly tied to the UI thread so that's not possible.

      Delete
    4. Thanks Stephen! This was very helpful!

      Delete
  22. I'm reading this two years too late! Thanks for the great articles, Stephen.

    ReplyDelete
  23. Is there a way to pass values like (ComboBox.Text) from the UI to an Async Method. I tried but i get cross thread error. BackgroundWorker has the e argument parameter can you advise me on hoe to achieve this without getting the error.

    ReplyDelete
    Replies
    1. You can choose whatever arguments you want for your async methods.

      Since you're getting a cross-thread error, I assume you're using something like Task.Run. You can just copy the value to a local variable and then pass it to the method, as such:
      string value = comboBox.Text;
      await Task.Run(() => MyMethodAsync(value));

      If this isn't clear, please post a question on Stack Overflow.

      Delete