小编典典

Do you have to put Task.Run in a method to make it async?

c#

I’m trying to understand async await in the simplest form. I want to create a
very simple method that adds two numbers for the sake of this example,
granted, it’s no processing time at all, it’s just a matter of formulating an
example here.

Example 1

private async Task DoWork1Async()
{
    int result = 1 + 2;
}

Example 2

private async Task DoWork2Async()
{
    Task.Run( () =>
    {
        int result = 1 + 2;
    });
}

If I await DoWork1Async() will the code run synchronously or asynchronously?

Do I need to wrap the sync code with Task.Run to make the method awaitable
AND asynchronous so as not to block the UI thread?

I’m trying to figure out if my method is a Task or returns Task<T> do I
need to wrap the code with Task.Run to make it asynchronous.

Stupid question I’m sure but I see examples on the net where people are
awaiting code that has nothing async within and not wrapped in a Task.Run or
StartNew.


阅读 260

收藏
2020-05-19

共1个答案

小编典典

First, let’s clear up some terminology: “asynchronous” (async) means that it
may yield control back to the calling thread before it starts. In an async
method, those “yield” points are await expressions.

This is very different than the term “asynchronous”, as (mis)used by the MSDN
documentation for years to mean “executes on a background thread”.

To futher confuse the issue, async is very different than “awaitable”; there
are some async methods whose return types are not awaitable, and many
methods returning awaitable types that are not async.

Enough about what they aren’t ; here’s what they are :

  • The async keyword allows an asynchronous method (that is, it allows await expressions). async methods may return Task, Task<T>, or (if you must) void.
  • Any type that follows a certain pattern can be awaitable. The most common awaitable types are Task and Task<T>.

So, if we reformulate your question to “how can I run an operation on a
background thread
in a way that it’s awaitable”, the answer is to use
Task.Run:

private Task<int> DoWorkAsync() // No async because the method does not need await
{
  return Task.Run(() =>
  {
    return 1 + 2;
  });
}

(But this pattern is a poor approach; see below).

But if your question is “how do I create an async method that can yield back
to its caller instead of blocking”, the answer is to declare the method
async and use await for its “yielding” points:

private async Task<int> GetWebPageHtmlSizeAsync()
{
  var client = new HttpClient();
  var html = await client.GetAsync("http://www.example.com/");
  return html.Length;
}

So, the basic pattern of things is to have async code depend on “awaitables”
in its await expressions. These “awaitables” can be other async methods or
just regular methods returning awaitables. Regular methods returning
Task/Task<T> can use Task.Run to execute code on a background thread,
or (more commonly) they can use TaskCompletionSource<T> or one of its
shortcuts (TaskFactory.FromAsync, Task.FromResult, etc). I don’t
recommend wrapping an entire method in Task.Run; synchronous methods should
have synchronous signatures, and it should be left up to the consumer whether
it should be wrapped in a Task.Run:

private int DoWork()
{
  return 1 + 2;
}

private void MoreSynchronousProcessing()
{
  // Execute it directly (synchronously), since we are also a synchronous method.
  var result = DoWork();
  ...
}

private async Task DoVariousThingsFromTheUIThreadAsync()
{
  // I have a bunch of async work to do, and I am executed on the UI thread.
  var result = await Task.Run(() => DoWork());
  ...
}

I have an async/await intro on my blog; at the end are some good followup resources. The
MSDN docs for async are unusually good, too.

2020-05-19