2

I need to generate a report once a number of methods have completed. But in this sample they are not asynchronous.

    static void Main(string[] args)
    {
        TaskMan();
    }

    async static void TaskMan()
    {
        Task t1 = m1();
        Task t2 = m2();
        await Task.WhenAll(t1, t2);
        Console.WriteLine("Complete");
    }

    async static Task m1()
    {
        decimal result = 0;
        for (int n = 1; n < 100000000; n++)
        {
            result += n;
        }
        Console.WriteLine(result);
    }

    async static Task m2()
    {
        decimal result = 0;
        for (int n = 1; n < 100000000; n++)
        {
            result += n;
        }
        Console.WriteLine(result);
    }

How to do them really async?

FreeVice
  • 2,567
  • 6
  • 21
  • 28

4 Answers4

5

Firstly, it isn't running asynchronously because in these lines you are actually calling the methods in a non-asynchronous manner:

Task t1 = m1();
Task t2 = m2();

This is because you neither await the call at this point, nor await internally. The net effect is a standard method call.

Secondly, async doesn't necessarily mean on another thread.

The following amendments will start new tasks and associate them with promises you can use to monitor their completion, then you WhenAll as before in an asynchronous manner. Do note that the task will start on StartNew, not on WhenAll.

async static void TaskMan()
{
    Task t1 = Task.Run((Action)m1);
    Task t2 = Task.Run((Action)m2);

    await Task.WhenAll(t1, t2);
    Console.WriteLine("Compete");
}

static void m1()
{
    decimal result = 0;
    for (int n = 1; n < 100000000; n++)
    {
        result += n;
    }
    Console.WriteLine(result);
}

static void m2()
{
    decimal result = 0;
    for (int n = 1; n < 100000000; n++)
    {
        result += n;
    }
    Console.WriteLine(result);
}

Unfortunately, we are not demonstrating the benefit of waiting asynchronously as we aren't doing anything in the mean time.

Adam Houldsworth
  • 63,413
  • 11
  • 150
  • 187
  • 1
    +1 one more note - `async` only shows that inside method should be one or more awaitings (otherwise compiler will complain) and it tells compiler to wrap results or exceptions into returned task object – Sergey Berezovskiy Jul 25 '13 at 09:12
  • 1
    @lazyberezovsky Thanks for the note, I had actually removed the other `async` keywords from the task methods as I also changed them to return `void`. – Adam Houldsworth Jul 25 '13 at 09:14
  • 1
    “you are actually calling the methods synchronously” I think that's a confusing way to put it. If the two methods were actually asynchronous, the same code would execute asynchronously. – svick Jul 25 '13 at 10:25
  • @svick I don't understand what you are trying to say there. I started with the OPs code and noticed that the method was called normally, as in not in an asynchronous manner. – Adam Houldsworth Jul 25 '13 at 10:27
  • 1
    @AdamHouldsworth Except when you're calling an `async` method normally, it usually does execute asynchronously. E.g. if you had `static async Task m1() { return await Task.Run(…); }`, then `Task t1 = m1();` wouldn't be synchronous. – svick Jul 25 '13 at 10:33
  • @svick Oh right I see, but none of that code featured in the question or my answer so I don't think I've confused it. I suppose an answer could have gone down that avenue. I'll have a look at rewording it. – Adam Houldsworth Jul 25 '13 at 10:35
  • @AdamHouldsworth how would you call a method in "an asynchronous manner"? There is no such thing as calling an awaitable method in "an asynchronous manner". Awaiting a task does not change how the method is called. If `m1` and `m2` was actually implemented as asynchronous methods, his code (with the exception of the `Main` method which doesn't wait) would be entirely correct. – Alxandr Jul 25 '13 at 10:41
  • @Alxandr Yes it would, but as it is the method call is inline without continuations, which is what I mean by an asynchronous manner. I am not entirely sure how else to word it. The presence of `async` on the method signature as it stood was not required, and his implementation of the methods internally did not delegate to a task / thread / awaitable - the effect being things were called sequentially. Awaiting a task, to my knowledge, causes different compilation output, which while not changing how its called at the lowest level, changes the style of the call. – Adam Houldsworth Jul 25 '13 at 10:47
  • My point is that there is nothing wrong with the call. Saying so is just plain wrong. The problem here is that `m1` and `m2` are not actually `async` methods, they just disguise themselves as such. Appending `async` to a method-declaration does not magically make it asynchronous or run on another thread. Because I'm reading from your answer that doing `Task t1 = m1(); Task t2 = m2();` (assuming that `m1` and `m2` was actually asynchronous in this case) is wrong, and would cause the methods `m1` and `m2` to run synchronously, which is wrong. – Alxandr Jul 25 '13 at 10:52
  • 1
    I just don't want other people think that one has to immediately await everything, or call `Task.Factory.StartNew` on every function to make things run asynchronous. This code would be just as wrong if it read `await m1(); await m2();`, which I think is an important point to make. – Alxandr Jul 25 '13 at 10:54
  • @Alxandr The way I saw those two calls was a failed attempt to assign a call target to a `Task` (for example, `Task t1 = new Task(m1)`), which instead was resulting in `m1` and `m2` being called and the return type of those methods being assign to `t1` and `t2`. When you look at it that way, there are a few ways it could have gone wrong and a few ways it could have been corrected. I chose to correct it from the way I saw the code running and the content of the `m1` and `m2` methods. I didn't say there was anything wrong with the call, I pointed out that it was doing something different. – Adam Houldsworth Jul 25 '13 at 10:57
  • @AdamHouldsworth "Awaiting a task, to my knowledge, causes different compilation output, which while not changing how its called at the lowest level, changes the style of the call." I'm not saying you're wrong, because this might simply be miscommunication and wrongly used/thought of consepts-names by either one of us, but to my understanding what you're saying is not right. Awaiting a task produces different output, yes, but it does not matter whether you await the task at the call-site or not. OP was already awaiting the task in the `Task.WhenAll` call. – Alxandr Jul 25 '13 at 10:59
  • @Alxandr Yes but the call was made and completed before then, so awaiting it with `WhenAll` was not having the effect he thought because the method was called without any continuations in place to make it appear asynchronous. I think this is just a terminology issue. – Adam Houldsworth Jul 25 '13 at 11:01
  • @AdamHouldsworth thanks for the detailed answer, but how can I get a my "Complete" message in the application's main thread? application exits without waiting for Tasks – FreeVice Jul 25 '13 at 11:32
  • 1
    @FreeVice `TaskMan().Wait();` – Alxandr Jul 25 '13 at 11:48
  • 1
    You should prefer `Task.Run` to `StartNew` in `async` code. [Here's why](http://blogs.msdn.com/b/pfxteam/archive/2011/10/24/10229468.aspx). – Stephen Cleary Jul 25 '13 at 11:48
2

There are two problems with your code.

First, as others have pointed out, async does not mean "run on a background thread". All that async does is enable a compiler-generated state machine that handles the await keyword. So async without await is useless. As @svick pointed out, don't ignore the compiler warnings! I have an async intro on my blog that you may find helpful. You should be using Task.Run to explicitly place work on a background thread.

The second problem is that you're starting asynchronous work but then returning from Main before giving it a chance to complete. If this is a simple proof-of-concept, then you could call Wait on your top-level task (normally, you would never want to mix blocking and asynchronous code like this as I describe in my MSDN article, but the Main method of a console application is an exception to that rule).

static void Main(string[] args)
{
    TaskManAsync().Wait();
}

static async Task TaskManAsync()
{
    Task t1 = Task.Run(() => m1());
    Task t2 = Task.Run(() => m2());
    await Task.WhenAll(t1, t2);
    Console.WriteLine("Complete");
}

static void m1()
{
    decimal result = 0;
    for (int n = 1; n < 100000000; n++)
    {
        result += n;
    }
    Console.WriteLine(result);
}

static void m2()
{
    decimal result = 0;
    for (int n = 1; n < 100000000; n++)
    {
        result += n;
    }
    Console.WriteLine(result);
}

If this is more than a proof-of-concept and you are actually intending to create an asynchronous Console application, then I recommend you use a single-threaded async-compatible context as I describe on my blog.

Stephen Cleary
  • 437,863
  • 77
  • 675
  • 810
1

The async keyword does not mean that method will be run in a asynchronous manner. I believe that you have VS report warnings on those methods as they don't do any awaiting. To run in a thread you should write:

return Task.Factory.StartNew(()=>
    decimal result = 0;
    for (int n = 1; n < 100000000; n++)
    {
        result += n;
    }
    Console.WriteLine(result);
});

this will do what you want.

Rafal
  • 12,391
  • 32
  • 54
-4

Use threading and create a separate thread for each function. You can be notified when the thread completes. Note that you cannot update the UI from a function called in a thread. You have to use other means of updating the UI itself from a thread, which might be a new question.

Your console.writeline should work tho.

Louis van Tonder
  • 3,664
  • 3
  • 31
  • 62
  • 2
    You shouldn't be manually creating threads with the exception of a few select edge cases, Tasks should be sufficient for almost everything now. – Trevor Pilley Jul 25 '13 at 10:27
  • Manually using a `Thread` is not a good idea. One of the reasons is that there *isn't* a good way to be notified when it completes. Also, this is clearly a console application, so I'm not sure why are you mentioning UI. – svick Jul 25 '13 at 10:28
  • Not to get off topic but a task is a thread? Sure there are scenarios where each can be used. Regarding the console app, yes, but is it not worth mentioning UI when telling someone about threading? As per my ( this might be another question) remark... – Louis van Tonder Jul 25 '13 at 10:51
  • No, a task is not a thread. It's left over to the implementation, that MIGHT (but not necessarily) pick an existing or new thread from a thread pool. There's a lot of overhead and extra complexity in managing your own threads. see: http://stackoverflow.com/questions/4130194/what-is-the-difference-between-task-and-thread – Wiebe Tijsma Jul 25 '13 at 11:29