0

I am starting to study C# TAP coding. I don't understand why the code is running synchronously

    async private void timer1_Tick(object sender, EventArgs e)
    {
        SyncCount++;
        result1.Text = SyncCount.ToString();

        AsyncCount = await CountForALongTimeAsync(AsyncCount);
        result2.Text = AsyncCount.ToString();
    }

    async Task<int> CountForALongTimeAsync(int counter)
    {
        Thread.Sleep(3000);
        counter++;
        return counter;
    }
Manfred Radlwimmer
  • 13,257
  • 13
  • 53
  • 62
M. Ko
  • 563
  • 6
  • 31
  • 7
    Change `Thread.Sleep` to `await Task.Delay`. – Sinatr Dec 07 '16 at 10:36
  • How do you understand it's synchronous or asynchronous? I see an async function returning its (passed by value) parameter increased by one... – Adriano Repetti Dec 07 '16 at 10:37
  • 2
    You should already be getting a warning saying that your `CountForALongTimeAsync` method has no `await` expressions, so will be running synchronously. It may well be that you should read a tutorial about how async works... it's not just a magic "add the async modifier and it all becomes asynchronous" feature. It's definitely worth understanding it reasonably thoroughly. – Jon Skeet Dec 07 '16 at 10:39
  • 2
    @GregaMohorko: No, it's not asynchronous, because `CountForALongTimeAsync` is effectively synchronous. This code *will* block the UI thread. – Jon Skeet Dec 07 '16 at 10:39
  • 1
    Possible duplicate of [How to get awaitable Thread.Sleep?](http://stackoverflow.com/questions/13429707/how-to-get-awaitable-thread-sleep) – Liam Dec 07 '16 at 10:40
  • @Liam not really a duplicate question (though I'm sure this is a duplicate of something out there) though the answer is the same. That's about how to get an awaitable sleep from someone who understood `await` and needed a `sleep` this is about not understanding `await` and using `sleep` as an example of how to delay thread-based multitasking. They're almost the opposite. – Jon Hanna Dec 07 '16 at 11:33

1 Answers1

6
async Task<int> CountForALongTimeAsync(int counter)
{

What comes next will be executed until the first awaited async call that actually does some waiting (it's possible that a given call could have all it needs to return immediately, e.g. a service that might hit the internet or might return data from a cache, in which case it won't wait).

There are no awaited calls at all, so the Task returned is returned already completed.

Since the calling await CountForALongTimeAsync is awaiting a task that is returned already completed, it runs synchronously.

The method would be better as:

async Task<int> CountForALongTimeAsync(int counter)
{
    await Task.Delay(3000);
    counter++;
    return counter;
}

Incidentally, the pre await way of doing something very (but not entirely) similar would have been:

Task<int> CountForALongTimeAsync(int counter)
{
    return Task.Delay(3000).ContinueWith(t =>
    {
        ++counter;
        return counter;
    });
}

Considering that these are different ideas of "continuing" after a task might or might not give some insight.

In contrast the closest pre-await way of doing what the code in your question does was:

Task<int> CountForALongTimeAsync(int counter)
{
    Thread.Sleep(3000);
    counter++;
    return Task.FromResult(counter); //FromResult returns an already completed task.
}
Jon Hanna
  • 110,372
  • 10
  • 146
  • 251