2

So I'm still trying to understand the async/await pattern, but I'm also trying to achieve the following behavior:

A method A calls method B which runs a number of processes. Some of those processes can be run on separate threads while other things are being processed so that their return values will be available closer to when they are needed. Method B needs to not return control to the caller until all of these processes are completed.

Here is the test code that I am working with:

static void Main(string[] args)
{
    CallProc();
    Console.WriteLine("Program finished");
    Console.ReadKey();
}

public static async Task CallProc()
{
    var two = Task.Factory.StartNew(() => SomeSynchronousProcessIDontOwn(5000, "two"));
    var one = Task.Factory.StartNew(() => SomeSynchronousProcessIDontOwn(500, "one"));
    var three = Task.Factory.StartNew(() => SomeSynchronousProcessIDontOwn(1500, "three"));

    // some process happens here

    var oneMessage = await one; // waits until one finishes and then snags it's value
    Console.WriteLine("Got message {0}", oneMessage);

    // some more stuff happens here

    var twoMessage = await two; // waits until two is finished and then snags it's value
    Console.WriteLine(twoMessage);

    // TODO: need to make sure that everything is completed before returning control to caller
}

public static string SomeSynchronousProcessIDontOwn(int delayTime, string message, bool delay = true)
{
    Console.WriteLine("Starting \"{0}\"", message);
    if(delay) Thread.Sleep(delayTime);
    return string.Format("Finished \"{0}\"", message);
}

Right now, what is happening is that everything words as I expected except that the method is returning before everything is finished, so the output shows "Program finished" while "two" is still running.

How do I write this so that CallProc() can execute those tasks asynchronously but delay returning until everything has been completed. In other words, CallProc() needs to run some tasks asynchronously, but CallProc() itself needs to be called synchronously.

13
  • 2
    Have you tried using a Task collection and use the Task.WaitAll() method? Commented Jul 29, 2014 at 20:28
  • I did. It yielded the same results. Control was returned to main() before everything finished Commented Jul 29, 2014 at 20:32
  • Why don't you just await three as well at the end? Commented Jul 29, 2014 at 20:33
  • 1
    I'm not going to suggest this as an answer, because I'm not confident, but don't you just want to have CallProc().Wait(); It's the same as James' answer really, but CallProc can still be called asynchronously. Commented Jul 29, 2014 at 20:51
  • 1
    @Hammerstein that was my intention with the use of WhenAll, the idea being CallProc could be awaited externally. Commented Jul 29, 2014 at 20:55

3 Answers 3

6

The idea of an asynchronous method, which is what you've written is that it will return control (approximately) immediately and the task that it returns will be marked as completed when the operation that it conceptually represents finishes.

This means that your program should either be looking at the resulting task to see when it finishes, or that you don't want an asynchronous method in the first place, and you should re-write CallProc synchronously rather than asynchronously.

To make CallProc synchronous simply remove async (and adjust the return type accordingly), and wait on each task instead of using await.

If CallProc really should be asynchronous then the caller should be adding a continuation (or using await) to perform an action when the task is completed, rather than when the method returns.

Sign up to request clarification or add additional context in comments.

2 Comments

So maybe I'm misunderstanding, but what I'm wanting tasks one two and three to start running immediately (essentially giving them a head start on processing) and then only await them as they're need (i.e. if you're still not finished by now, then I'll wait, otherwise gimme the result and carry on). CallProc is only marked async so that I can use the await keyword in this fashion
@Sinaesthetic await and Wait mean completely different things. Wait means, "Block the thread until the task completes." await means, "Return control of execution back to the caller, and execute the rest of the code in this method when the task that is being awaited finishes." If you want the method to be syncrhonous, you use Wait. If you want the method to be asynchronous, you use await.
5

Instead of awaiting each task individually why not just await all of them using WhenAll

public static async Task CallProc()
{
    var two = Task.Factory.StartNew(() => SomeSynchronousProcessIDontOwn(5000, "two"));
    var one = Task.Factory.StartNew(() => SomeSynchronousProcessIDontOwn(500, "one"));
    var three = Task.Factory.StartNew(() => SomeSynchronousProcessIDontOwn(1500, "three"));

    // run synchronous tasks

    await Task.WhenAll(one, two, three);
}

If you would prefer to have CallProc block (i.e. not return until all tasks have finished) then remove the async declaration and use Task.WaitAll instead.

public static void CallProc()
{
    // start tasks

    Task.WaitAll(one, two, three);
}

6 Comments

@Servy yeah, but instead of waiting for tasks individually, why not just wait for them all to complete?
Because he's doing things after each await besides just awaiting the next task, or so the comments in the code state.
@Servy the OP didn't mention that those tasks need to be run in any particular order (I have asked for clarity).
They aren't run in a particular order. They're performed in parallel, but the results of each task are performed in order, and don't necessarily need to wait until the other tasks are done. Regardless, that's not really the point here. Even if using WhenAll was more convenient, it doesn't solve the fact that his program doesn't function as he expects, and you're suggesting a purely aesthetic change that won't affect the underlying behavior he is concerned about. So this absolutely fails to answer the question, regardless of whether or not this is a valid refactor in context.
This would in no way prevent the application from finishing, and the use of WaitAll alone would not be correct. It's important that the code be either entirely synchronous or entirely asynchronous. Mixing the two as you propose would be very bad.
|
1

One way to do this is to simply call Wait() on the result of CallProc. This will essentially wait for the task returned by CallProc to finish before it continues. Calling Wait in a GUI app can cause a deadlock but for a console app it is fine.

static void Main(string[] args)
{
    CallProc().Wait();
    Console.WriteLine("Program finished");
    Console.ReadKey();
}

That will ensure that "Program finished" is printed after task two is finished.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.