6

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?

noseratio
  • 59,932
  • 34
  • 208
  • 486
olf
  • 860
  • 11
  • 23
  • 2
    You're mixing asynchronous with multitasking. It's not the same. – MarcinJuraszek May 12 '14 at 07:47
  • As @MarcinJuraszek said, try switching `Thread.Sleep` to something async instead, like `WebClient.DownloadStringTaskAsync()`. And await all threads at the same time, not one by one. `async` is not multitaskning. – flindeberg May 12 '14 at 08:19

3 Answers3

5

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?"

noseratio
  • 59,932
  • 34
  • 208
  • 486
2

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".

Christoph Fink
  • 22,727
  • 9
  • 68
  • 113
  • @ssg That's the only way to get it run in 5 seconds. And the reason is, you need multitasking to make that happen, async is not enough. – MarcinJuraszek May 12 '14 at 07:51
  • @chrfin he wants the total delay to be split into smaller chunks and executed simultaneously. he doesn't mention "threading" specifically. and the question title specifically says "asynchronous", which is not about threading at all. – Sedat Kapanoglu May 12 '14 at 07:54
  • @MarcinJuraszek no it's not the only way for the given example. see my answer. – Sedat Kapanoglu May 12 '14 at 07:54
  • @ssg: No, if you `await` them it will always run one after the other as this is the puropse of `await`... – Christoph Fink May 12 '14 at 07:56
  • @ssg I didn't mention threads either. Your answer does not show the way to achieve results OP expects (program to run in 5 seconds). – MarcinJuraszek May 12 '14 at 07:57
  • @MarcinJuraszek Not if you use `Task.WaitAll` to wait for them all at once, see my last edit. – Sedat Kapanoglu May 12 '14 at 07:59
  • 1
    @ssg Yeah, after couple edits, several comments and 15 minutes your answer finally shows a way to go. Good job! – MarcinJuraszek May 12 '14 at 08:01
  • @MarcinJuraszek add the satisfaction of proving you wrong about "That's the only way to get it run in 5 seconds" :) – Sedat Kapanoglu May 12 '14 at 08:02
  • @ssg eeee, you used exact same thing my comment was about (multitasking). I don't get why you're satisfied, because you didn't prove me wrong :) but anyway, enough of offtop for tonight. – MarcinJuraszek May 12 '14 at 08:05
  • @MarcinJuraszek You said multitasking is the only way to do it. I said no. My answer does not use multitasking. It uses async only. I rest my case :) – Sedat Kapanoglu May 12 '14 at 08:10
  • Wow, whats going on here?! I have accepted both of the above answers to be correct: The first one did make my app run in about 5 seconds, but my stopwatch stopped working (which was okay - it was just for "fun"). The second answer also works - and didn't ruin my stopwatch. Thx for quick responses all! – olf May 12 '14 at 08:19
  • @ssg: Your answer also uses multitasking "under the hood". Without it it IS NOT POSSIBLE as you can NOT run two task in parallel using SINGLEtasking (is that how its called?) - hence the word MULTItasking. The word itself does not specifiy "how" it is done, but as soon as two things (=tasks) run in parallel you are using multitasking, even if the "task in parallel" are just "waiting"... – Christoph Fink May 12 '14 at 08:24
  • @chrfin No it does not use multithreading. I said "Task.Run uses threading not asynchronous execution". And Marcin specifically said it wouldn't be possible in any other way, implying he used the term "multitasking" specifically for "threading". Asynchronous could allow you to perform tasks simultaneously but it's not about multithreading. My answer runs in a single thread yet executes simultaneously. – Sedat Kapanoglu May 12 '14 at 08:36
  • @olf if you are ok to use multithreading (that is to use multiple OS threads to split a job), it does have nothing to do with asynchronous execution (which uses a single thread to perform multiple operations simultaneously), just so you know. – Sedat Kapanoglu May 12 '14 at 08:39
  • @ssg: You now are mixing to similar but not equal terms: multiTASKING and multiTHREADING. In your comment above you said you are not using multiTASKING which you do. But now you are talking about multiTHREADING which you possibly don't (but not definitely - the task-scheduler could run it on different threads). The term multiTASKING just descripbes "doing two things in parallel" and does NOT specify "how" this is done, so "waiting for two things in the same thread" already "qualifies". In contrast multiTHREADING describes doing things in parallel using multiple threads. – Christoph Fink May 12 '14 at 08:43
  • @chrfin That's fine. So what do you think Marcin meant when he said "Multitasking is the only way" regarding my comment "Task.Run uses threading, not async execution"? Did he just want to state the obvious? Or did he mean it as a synonym for "multithreading"? I think the latter. What do you think? – Sedat Kapanoglu May 12 '14 at 08:46
  • @ssg: I am not sure what Marcin meant, but I was answering to your comment where you stated `My answer does not use multitasking.` which is wrong, as it does use `multitasking` but not definitely `multithreading` (but very well could use it without you knowing, as the task-scheduler could switch threads when doing `async`/`await`), but we are getting a little to much into "semantic details" here IMO... – Christoph Fink May 12 '14 at 08:51
  • @chrfin: You're right about that. As I said, I started with the premise that Marcin used it as a synonym for multithreading, hence I started using the terms interchangeably. Do you think Marcin's comment makes sense if he meant "any kind of multitasking"? Even in the light of the fact that I had never claimed that it was possible without any kind of multitasking? – Sedat Kapanoglu May 12 '14 at 08:57
  • @ssg: As I already said, I am not sure if he really meant `not possible without multitasking` (which would be correct) or as a synonym for `not possible without multithreading` (which is not correct for the example above). But all that aside it is not very likely that the OP really wants to "wait 3 times in parallel", but want do do "stuff in parallel" and there you need multihreading to do it... – Christoph Fink May 12 '14 at 09:03
  • @chrfin not if the aforementioned stuff is I/O heavy. Multiple I/O operations can be executed simultaneously on a single thread using async. – Sedat Kapanoglu May 12 '14 at 09:08
1

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.

Yishai Galatzer
  • 8,791
  • 2
  • 32
  • 41
  • I am going to change an existing application with some rather heavy SQL calls into async calls. So I just wanted to be 100% sure that my async-setup was correct before starting changing my real application. – olf May 12 '14 at 08:13
  • Is your application an ASP.NET application or a console application? Or the question is are you trying to lower the latency of your app (console or asp.net), or make it handle more requests per second (asp.net). – Yishai Galatzer May 12 '14 at 15:52