3

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 ?

4 Answers 4

6

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

Sign up to request clarification or add additional context in comments.

2 Comments

With the additional change of making DoHeavyWork return bool instead of Task<bool>.
I had thought that that was somewhat implied :D Love your website btw.
4

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:

3 Comments

Code that wraps the entire method body in a Task.Run is lying about its behavior; it's exposing an asynchronous API for a fake-asynchronous method. Wrapping the call to the method within Task.Run is more maintainable in the long term.
@StephenCleary, I don't understand what you mean with "fake-asynchronous". Isn't everything inside the lambda passed to Task.Run() being run asynchronously? I agree with your point on maintainability though. The intention was to wrap a single call, but I used the intermediate result variable to make it more explicit how Task is being used (i.e. inside the lambda one simply returns a value of the type indicated by the Task's type parameter. I changed the code sample a little bit.
"Asynchronous" can mean a few different things. I call pushing work to a background thread "fake asynchronous", because all the code is doing is freeing up the current thread by using another thread. In contrast, "true asynchronous" code does not use any threads (one example is asynchronous I/O). In the op's case, there's (presumably) only synchronous work to do, so making the method signature async is misleading IMO. I have a longer description about using Task.Run as a method implementation on my blog
1

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

Comments

0

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.