bubarnet
bubarnet

Reputation: 101

Misunderstanding of async/await functions

I work with C#/VS2012/.Net4.0/Microsoft.Bcl.async NuGet package

After a previously question, I try to use async/await to avoid freezing UI. But each of my test is unsuccessfull.

The main function, which does a heavy work, was like this :

public bool PopulateTV()
{
  using (WaitingForm waitingForm=new WaitingForm())
  {
    waitingForm.Show();
    bool boolResult=DoHeavyWork(); //UI is freezing, waitingForm() doesn't show correctly
    return boolResult;
  }
}

A call to this function is like

if(!PopulateTV())
{
}

I try to implement the async/await

public async Task<bool> populateTV()
{
  using (WaitingForm waitingForm=new WaitingForm())
  {
    Task<bool> func=DoHeavyWork();
    bool boolResult=await func;
    return boolResult;
  }
}

public Task<bool> DoHeavyWork()
{
  //do the work
  return boolResult;
}

Of course, I get an error in DoHeavyWork, as I return a bool and not Task<bool>. So I change the DoHeavyWork function :

public async Task<bool> DoHeavyWork()
{
  //do the work
  return boolResult;
}

This time, no error, but a warning (I use a non english version of VS, so I translate the error/warning message) :

This async method doesn't have an await operator, so it will execute as synchronous.

It's only a warning, but why do I get this message ?

But it's not the big problem. When I call the parent method PopulateTV(), I want to return a bool value (if(!PopulateTV()) ... And I get an error :

! operator can't be used with 'System.Threading.Tasks.Task'.

Of course. But PopulateTV() return a bool (return boolResult;), even if the method declaration use Task<bool>.

Where is my error ?

Upvotes: 3

Views: 329

Answers (4)

Good Night Nerd Pride
Good Night Nerd Pride

Reputation: 8500

fk2's answer is correct, but you can avoid overhead and fragility by simply returning a Task<bool> directly:

public Task<bool> DoHeavyWork()
{
    return Task.Run(() =>
    {
        return HeavyWorkReturningBool();
    });
}

See this question for some more details.


However, as pointed out in the comments, this is not a clean solution. Offloading work to background threads to preserve UI responsiveness belongs to the UI code and not the API implementing the work. For a more modular solution you should implement it this way:

public async bool PopulateTV()
{
    using (var waitingForm = new WaitingForm())
    {
        waitingForm.Show();
        return await Task.Run(() => DoHeavyWork());
    }
}

DoHeavyWork remains a regular, synchronous Method:

public bool DoHeavyWork()
{
    var boolResult;
    //do the work
    return boolResult;
}

See the following blog posts for more information about this:

Upvotes: 4

Mat&#237;as Fidemraizer
Mat&#237;as Fidemraizer

Reputation: 64943

But it's not the big problem. When I call the parent method PopulateTV(), I want to return a bool value (if(!PopulateTV()) ... And I get an error :

It's if(!(await PopulateTV())). First wait for the result (bool) and later evaluate it.

In the other hand, your not that async method is equivalent to code it this way:

public Task<bool> DoHeavyWork()
{
  //do the work
  return Task.FromResult(true);
}

async/await keywords are meant to work as syntactic sugar in order to re-write your task-based asynchronous pattern code into a more readable one.

Actually, if your DoHeavyWork does something like this:

public async Task<bool> DoHeavyWork()
{
  await Task.Factory.StartNew(() => Thread.Sleep(10000));

  return true;
}

...is almost the same as writing it this way:

    public Task<bool> DoHeavyWork()
    {
        return Task.Factory.StartNew
        (
             () =>
             {
                  Thread.Sleep(10000);

                  return true;
             }
        )

    }

You need to understand that async/await is just a compiler feature to let you code elegant asynchronous code, but it doesn't mean that the async method must be asynchronous at all. It means that a Task instance can be awaited with await keyword.

That is, you can implement methods with asynchronous signatures (i.e. Task MyMethod() { }) but fake them returning a Task generated by a TaskCompletionSource<T>:

public Task<bool> DoHeavyWork()
{
  // No async operation here. This is just a fake!
  TaskCompletionSource<bool> completion = new TaskCompletionSource<bool>();
  completion.SetResult(true);

  return completion.Task;
}

...and this can be also simplified using Task.FromResult<T>(T result), as you can check in some of this answer's code snippets.

So, why I need to develop my APIs using task-based asynchronous pattern (TAP)?

Because the caller of your methods shouldn't need to know if the method is synchronous or asynchronous. Method's implementation will decide it, but at the same time the caller can work asynchronously if the whole method is actually asynchronous.

If you do the opposite (sync methods), your entire code flow will need to be changed if you decide to implement everything as asynchronous operations:

// I don't know if it's actually async or not, I don't care. The calling method
// decides it.
bool result = await DoWorkAsync();

// I know it's sync. So my code can become pure garbage if I need to make it
// synchronous since it can't be awaited and it doesn't return a Task instance!
bool result = DoWork();

Upvotes: 1

Dennis_E
Dennis_E

Reputation: 8904

But PopulateTV() return a bool (return boolResult;), even if the method declaration use Task<bool>.

Wrong. PopulateTV() returns a Task<bool>

//        (return Type is here)
//           ||||||||||
//           vvvvvvvvvv
public async Task<bool> populateTV()

Just think of return boolResult; as being the return statement of the Task, not the method. To get the actual bool result, you need to await that Task:

if (!(await PopulateTV()))

Why are you getting a warning when you declare your method like this?

public async Task<bool> DoHeavyWork()

Because you are using async without await. You don't need the async keyword here. All async does is enable await, so if you don't use await, don't use async. You can just return a Task:

public Task<bool> DoHeavyWork()
{
    return Task.Run<bool>(() => 
        //do the heavy work
        return boolResult;
    );
}

Upvotes: 0

fk2
fk2

Reputation: 749

You are not actually running your heavy-lifting code in a seperate thread - async does not automatically guarantee that.

Try something like (await Task.Run(() => DoHeavyWork()));

Upvotes: 6

Related Questions