async method in C# not asynchronous?

2019-06-20 01:17发布

Having created the following console application I am a little puzzled why it seems to run synchronously instead of asynchronously:

class Program
{
    static void Main(string[] args)
    {
        Stopwatch stopwatch = new Stopwatch();
        stopwatch.Start();
        var total = CreateMultipleTasks();
        stopwatch.Stop();

        Console.WriteLine("Total jobs done: {0} ms", total.Result);
        Console.WriteLine("Jobs done in: {0} ms", stopwatch.ElapsedMilliseconds);
    }

    static async Task<int> CreateMultipleTasks()
    {
        var task1 = WaitForMeAsync(5000);
        var task2 = WaitForMeAsync(3000);
        var task3 = WaitForMeAsync(4000);

        var val1 = await task1;
        var val2 = await task2;
        var val3 = await task3;

        return val1 + val2 + val3;

    }

    static Task<int> WaitForMeAsync(int ms)
    {
        Thread.Sleep(ms);
        return Task.FromResult(ms);
    }
}

When running the application, output is:

Total jobs done: 12000 ms
Jobs done in: 12003 ms

I would have expected somehing like:

Total jobs done: 12000 ms
Jobs done in: 5003 ms

Is this because when I use the Thread.Sleep method it stops further execution of the whole application? Or am I missing something important here?

3条回答
再贱就再见
2楼-- · 2019-06-20 02:10

Even when you convert to using Task.Run or Task.Delay as other answers suggest, you should avoid using the blocking Task.WaitAll anywhere inside async methods, as much as you can. Mixing asynchronous and synchronous code is usually a bad idea, it increases the number of redundantly blocked threads and promotes deadlocks.

Instead, use await Task.WhenAll and move the blocking wait to the top level (i.e., Main method in this case):

class Program
{
    static void Main(string[] args)
    {
        Stopwatch stopwatch = new Stopwatch();
        stopwatch.Start();
        var total = CreateMultipleTasks();

        total.Wait();

        stopwatch.Stop();

        Console.WriteLine("Total jobs done: {0} ms", total.Result);
        Console.WriteLine("Jobs done in: {0} ms", stopwatch.ElapsedMilliseconds);
    }

    static async Task<int> CreateMultipleTasks()
    {
        var task1 = Task.Run(() => WaitForMeAsync(5000));
        var task2 = Task.Run(() => WaitForMeAsync(3000));
        var task3 = Task.Run(() => WaitForMeAsync(4000));

        await Task.WhenAll(new Task[] { task1, task2, task3 });

        return task1.Result + task2.Result + task3.Result;
    }

    static int WaitForMeAsync(int ms)
    {
        // assume Thread.Sleep is a placeholder for a CPU-bound work item
        Thread.Sleep(ms);
        return ms;
    }
}

On a side note, check Stephen Toub's "Should I expose asynchronous wrappers for synchronous methods?" and "Should I expose asynchronous wrappers for synchronous methods?"

查看更多
小情绪 Triste *
3楼-- · 2019-06-20 02:12

You run the task in a synchounus manner. You can do something like this:

static async Task<int> CreateMultipleTasks()
{
    var task1 = Task.Run<int>(() => WaitForMeAsync(5000));
    var task2 = Task.Run<int>(() => WaitForMeAsync(3000));
    var task3 = Task.Run<int>(() => WaitForMeAsync(4000));

    Task.WaitAll(new Task[] { task1, task2, task3 });

    return task1.Result + task2.Result + taks3.Result;

}

Using the three await in a row will NOT run the tasks in parallel. It will just free the thread while it is waiting (if you use await Task.Delay(ms) as Thread.Sleep(ms) is a blocking operation), but the current execution will NOT continue with task2 while task1 is "sleeping".

查看更多
神经病院院长
4楼-- · 2019-06-20 02:12

Your WaitForMeAsync method is just a simple sync method pretending to be an async one.

You don't perform anything async and Sleep() just blocks the thread.

Why would you want to delay async? (yes you can use await Task.Delay(ms)), need more input to help there.

查看更多
登录 后发表回答