36

After looking around on both Async/Await and Threading, I'm still unsure of the right way to apply it to my situation. No matter the variation that I try my UI still hangs because I don't seem to be calling my desired function asynchronously, additionally, I may in fact need threading for my solution.

What I'm trying to do: I have a WPF application on which there is a button that I would like to start an operation that still allows interaction with the program, through UI or otherwise. Once a condition is met that is determined outside of this function, the function should end. To me this sounds fairly standard but I have a feeling I'm misunderstanding something and I've implemented it incorrectly.

What I have right now:

private async void start_button_Click(object sender, RoutedEventArgs e)
{
    await StaticClass.MyFunction();
}

private void stop_button_Click(object sender, RoutedEventArgs e)
{
    StaticClass.stopFlag = true;
}

public static Task<int> myFunction()
{
    //Stuff Happens

    while(StaticClass.stopFlag == false)
        //Do Stuff

    //Stuff Happens

    return Task.FromResult(1) //I know this is bad, part of the reason I'm asking
}

I was hoping for some guidance on if I'm approaching this the right way and any insight on what I'm doing wrong.

Karoly S
  • 3,180
  • 4
  • 35
  • 55
  • **A complete example**: [How to execute task in the wpf background while able to provide report and allow cancellation?](http://stackoverflow.com/a/21357567/1768303) – noseratio Jan 29 '14 at 06:17

4 Answers4

50

You've definitely implemented it incorrectly. You're returning a Task<int>, but only once all the work has already been done.

It seems to me that you should probably just have a synchronous method:

private static void MyFunction()
{
    // Loop in here
}

Then start a task for it like this:

Task task = Task.Run((Action) MyFunction);

You can then await that task if you want - although in the example you've given, there's no point in doing so, as you're not doing anything after the await anyway.

I'd also agree with Reed that using a CancellationToken would be cleaner than a static flag somewhere else.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • Good one! Just do give Karoly some other hint, which helps me in separating this: Because so many sources say something like "... await starts a new thread ..." people think that await does all the magic stuff. It helped me a lot to think of await as a helper function for not messing with waiting for the result. Threading still rulez! – Alexander Schmidt Jan 28 '14 at 23:46
  • @sprinter252: Do they? I haven't seen any articles claiming that await starts a new thread. I've seen people *assume* it does, but no real *sources* claiming that it does. – Jon Skeet Jan 28 '14 at 23:47
  • Okay, things are making a bit more sense now, thanks Jon! Follow up question for ya, whats the difference between your second code snippet and @ScottChamberlain's second code snippet? Does yours just handle a return value of Task? If I don't actually need to return anything would your recommendation change? – Karoly S Jan 29 '14 at 00:15
  • @KarolyS: Nothing, really - it's just that Scott's inlined the loop whereas I've kept it in a separate method. – Jon Skeet Jan 29 '14 at 00:18
  • @JonSkeet Yes. I'll try to find some so that you get something to laugh about. – Alexander Schmidt Jan 29 '14 at 23:51
  • Thanks @JonSkeet Of course, Task.Run method is only on .Net 4.5 :) – ABS May 10 '15 at 06:37
  • @AliBagheriShakib: Yes, but the OP is already using `async` which is at least suggesting that they're using .NET 4.5. If they're using the async targeting pack, I'm not sure whether that *also* includes Task.Run - I wouldn't be surprised. – Jon Skeet May 10 '15 at 06:38
  • @JonSkeet how to pass parameters to function? – TejpalBh Jun 05 '19 at 10:51
  • @TejpalBh: This should be a new question, including what you've tried so far. – Jon Skeet Jun 05 '19 at 13:27
11

You did misunderstand.

public static Task<int> myFunction()
{
    //Stuff Happens

    while(StaticClass.stopFlag == false)
        //Do Stuff

    //Stuff Happens

    return Task.FromResult(1) //I know this is bad, part of the reason I'm asking
}

All of that code still happens in the intial await StaticClass.MyFunction(); call, it never returns control to the caller. What you need to do is put the loop portion in to a separate thread.

public static async Task myFunction()
{
    //Stuff Happens on the original UI thread

    await Task.Run(() => //This code runs on a new thread, control is returned to the caller on the UI thread.
    {
        while(StaticClass.stopFlag == false)
            //Do Stuff
    });

    //Stuff Happens on the original UI thread after the loop exits.
}
Scott Chamberlain
  • 124,994
  • 33
  • 282
  • 431
3

Instead of trying to use a bool for this, you should consider using the managed cancellation framework built into the framework.

Basically, you'd build a CancellationTokenSource, and pass a CancellationToken to your method which could be used to handle cancellation.

Finally, your current method will never get off the UI thread. You'd need to use Task.Run or similar to move the method to the ThreadPool if you don't want to block the UI.

Reed Copsey
  • 554,122
  • 78
  • 1,158
  • 1,373
3

As an alternative, you could look into using BackgroundWorker class to do the job, your UI will stay interactive.

Eugene
  • 2,965
  • 2
  • 34
  • 39