Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# Threading/Async: Running a task in the background while UI is interactable

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.

like image 468
Karoly S Avatar asked Jan 28 '14 23:01

Karoly S


3 Answers

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.

like image 109
Jon Skeet Avatar answered Oct 10 '22 23:10

Jon Skeet


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.
}
like image 33
Scott Chamberlain Avatar answered Oct 10 '22 22:10

Scott Chamberlain


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.

like image 3
Reed Copsey Avatar answered Oct 10 '22 23:10

Reed Copsey