5

I am trying to learn the async and await features of .NET 4.5 .First of all here's my code

    static async void Method()
    {
        await Task.Run(new Action(DoSomeProcess));
        Console.WriteLine("All Methods have been executed");
    }

    static void DoSomeProcess()
    {
        System.Threading.Thread.Sleep(3000);
    }

    static void Main(string[] args)
    {
        Method();
        //Console.WriteLine("Method Started");
        Console.ReadKey();
    }

This code doesn't give me any results on the console. I cant understand why. I mean aren't tasks suppose be just threads that aren't blocking. However if i uncomment the Console.WriteLine() in the main method everything seems to be working fine.

Can anybody tell me what's going on here ?

Win Coder
  • 6,628
  • 11
  • 54
  • 81
  • 1
    This works as expected for me, It prints *All Methods have been executed* to console after 3 seconds' – Sriram Sakthivel Oct 12 '13 at 14:54
  • ummm weird. Unless i Uncomment the Console.WriteLine in main i get nothing on the console after 3 seconds. – Win Coder Oct 12 '13 at 14:57
  • In general, you should avoid `async void` methods, because there is no way to wait for their completion. But in this specific case, your code will work fine (unless you press Enter before the sleep completes). – svick Oct 12 '13 at 14:58
  • Uncommenting that also works fine :) – Sriram Sakthivel Oct 12 '13 at 14:59
  • I inserted a Breakpoint on WriteLine in Method() it seems the thread reaches there but for some reason doesn't display on the console. Why is this happening – Win Coder Oct 12 '13 at 14:59
  • Ok i think why my program isn't displaying something http://stackoverflow.com/a/9208975/1762761 its because after calling Method() in main it has exited. – Win Coder Oct 12 '13 at 15:08
  • Another small change is to only use the static class containing the entry point for entry. Similar to the example .NET provides for WinForm applications, create a class and use it from main(). This will remove the need to make all of your methods static, which in this case of using TaskParallelLibrary (TPL) will make life much easier. – paegun Oct 12 '13 at 15:41
  • @paegun I don't see why. Static methods work with TPL just fine. – svick Oct 12 '13 at 16:06
  • @svick yes, TPL handles static just fine. However, the point will quickly come when the TPL using code is maintaining the state of variables. And at that point, scope sure helps. See here for example: http://stackoverflow.com/questions/8370144/should-i-use-static-function-in-c-sharp-where-many-calls-the-same-func – paegun Oct 12 '13 at 16:26
  • possible duplicate of [Async/await not reacting as expected](http://stackoverflow.com/questions/7892360/async-await-not-reacting-as-expected) – J. Lennon Oct 14 '13 at 16:19

2 Answers2

13

With the async/await pattern some things are different as previously with threads.

  1. you shouldn't use System.Threading.Thread.Sleep because this is still blocking and does not work with async. Instead use Task.Delay

  2. Consider making all your code async. Only the Main method in your console cannot be async of cause

  3. Avoid async void methods. Basically async void is just meant for event handlers which cannot return something. All other async methods should return Task or Task<T>

Modified your example:

    static async Task Method()
    {
        await DoSomeProcess();
        Console.WriteLine("All Methods have been executed");
    }

    static async Task DoSomeProcess()
    {
        await Task.Delay(3000);
    }

Now change your Main method because this should be the place where you start your task

    Task.Run(() => Method()).Wait();
    //Console.WriteLine("Method Started");
    Console.ReadKey();
MichaC
  • 13,104
  • 2
  • 44
  • 56
4

There is a small learning curve to learn how to use the async and await keywords correctly..

The problem is that nobody waits whom is waiting, there are some other details such as the SyncronizationContext and Task:

you should check some articles: http://msdn.microsoft.com/en-us/library/vstudio/hh191443.aspx

To use async and await keywords in Console, you need an extra code: Can't specify the 'async' modifier on the 'Main' method of a console app or Await a Async Void method call for unit testing or Unit Test Explorer does not show up Async Unit Tests for metro apps

I usually do this approach:

static void Main(string[] args)
{
    Console.WriteLine("HELLO WORLD");
    var t1 = Task.Factory.StartNew(new Func<Task>(async () => await Method()))
        .Unwrap();
    Console.WriteLine("STARTED");
    t1.Wait();
    Console.WriteLine("COMPLETED");
    Console.ReadKey();
}

static async Task Method()
{
    // this method is perfectly safe to use async / await keywords
    Console.WriteLine("BEFORE DELAY");
    await Task.Delay(1000);
    Console.WriteLine("AFTER DELAY");
}
Community
  • 1
  • 1
J. Lennon
  • 3,311
  • 4
  • 33
  • 64
  • Why are you creating another task? Isn't the one returned by `Method` good enough? – Paulo Morgado Oct 13 '13 at 22:12
  • 1
    I need to encapsulate a task, in order to use its SynchronizationContext, if I do not, depending on the current context the text "Completed" will be able to show before "After Delay" – J. Lennon Oct 14 '13 at 02:14
  • @Servy sorry, its true, I also do not want people to confuse things, but its an "artifice" when used the "Unwrap" method everything that happens then respects the async/await keywords. – J. Lennon Oct 14 '13 at 03:40
  • @J.Lennon, there is no `SynchronizationContext` on a console application. And even if there was on, what uses the `SynchronizationContext` is the compiler generated state machine when `async`/`await`. – Paulo Morgado Oct 14 '13 at 06:51
  • @J.Lennon Your async anonymous method doesn't *do* anything. This example would do *exactly* the same thing if you just put `Method` directly as the delegate into `StartNew`. Note that you shouldn't use `StartNew` with an async method. You should use `Task.Run`, as it will unwrap the returned task for you. Currently you are not doing so manually, so when `t1` completes it just means that it finished starting the other task, not that it finished the inner task. – Servy Oct 14 '13 at 14:12
  • @Servy by chance you tested my code? Your judgment is wrong (my goal in the example was used the async/await keywords and should work regardless of the scenario, if you create some complexity and some logic around inside the `Method` will work perfectly as expected, and if you make the code different, the keywords maybe not respect the order of execution and completion, you've read about the differences of `Run` and `StartNew`? I know we're talking about .NET 4.5, but my solution also works for .NET 4.0 also. – J. Lennon Oct 14 '13 at 16:01
  • 3
    @J.Lennon The `Unwrap` was off required scrolling to the right to see. As it was it was the only thing not shown on the screen, so I didn't notice it. That said, your `async` anonymous method is still pointless. The code would be much simpler, readable, and effective if you just wrote `t1 = Method();` – Servy Oct 14 '13 at 16:35