C#Threading / Async:在UI可交互的情况下在后台运行任务

时间:2022-11-06 20:51:58

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.

在浏览Async / Await和Threading之后,我仍然不确定将它应用于我的情况的正确方法。无论我尝试我的UI的变化仍然挂起,因为我似乎不是异步调用我想要的函数,另外,我可能实际上需要线程为我的解决方案。

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.

我正在尝试做的事情:我有一个WPF应用程序,其中有一个按钮,我想开始一个操作,仍然允许通过UI或其他方式与程序交互。一旦满足在此函数之外确定的条件,该函数应该结束。对我来说,这听起来相当标准,但我有一种感觉,我误解了一些东西,我已经错误地实现了它。

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.

我希望得到一些指导,如果我正在以正确的方式接近这一点以及对我做错的任何见解。

4 个解决方案

#1


20  

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

你肯定错误地实现了它。您将返回一个Task ,但只有在完成所有工作后才会返回。

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.

我也同意里德的观点,使用CancellationToken比其他地方的静态标志更清晰。

#2


7  

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.

所有这些代码仍然发生在初始await StaticClass.MyFunction();呼叫,它永远不会将控制权返回给呼叫者。您需要做的是将循环部分放入单独的线程中。

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.
}

#3


3  

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

您应该考虑使用框架内置的托管取消框架,而不是尝试使用bool。

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

基本上,您将构建一个CancellationTokenSource,并将CancellationToken传递给您的方法,该方法可用于处理取消。

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.

最后,您当前的方法永远不会脱离UI线程。如果您不想阻止UI,则需要使用Task.Run或类似方法将方法移动到ThreadPool。

#4


1  

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

作为替代方案,您可以考虑使用BackgroundWorker类来完成工作,您的UI将保持交互。

#1


20  

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

你肯定错误地实现了它。您将返回一个Task ,但只有在完成所有工作后才会返回。

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.

我也同意里德的观点,使用CancellationToken比其他地方的静态标志更清晰。

#2


7  

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.

所有这些代码仍然发生在初始await StaticClass.MyFunction();呼叫,它永远不会将控制权返回给呼叫者。您需要做的是将循环部分放入单独的线程中。

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.
}

#3


3  

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

您应该考虑使用框架内置的托管取消框架,而不是尝试使用bool。

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

基本上,您将构建一个CancellationTokenSource,并将CancellationToken传递给您的方法,该方法可用于处理取消。

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.

最后,您当前的方法永远不会脱离UI线程。如果您不想阻止UI,则需要使用Task.Run或类似方法将方法移动到ThreadPool。

#4


1  

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

作为替代方案,您可以考虑使用BackgroundWorker类来完成工作,您的UI将保持交互。