С# Threading/Async: запуск задачи в фоновом режиме, в то время как пользовательский интерфейс является интерактивным

Посмотрев вокруг как на Async/Await, так и на Threading, я все еще не уверен, как правильно применить его к моей ситуации. Независимо от того, какую вариацию я пытаюсь использовать, мой пользовательский интерфейс все еще висит, потому что я, похоже, не асинхронно вызываю свою желаемую функцию, кроме того, на самом деле мне может понадобиться потоковая передача для моего решения.

То, что я пытаюсь сделать: У меня есть приложение WPF, на котором есть кнопка, которую я хотел бы запустить с помощью операции, которая по-прежнему позволяет взаимодействовать с программой, через интерфейс или иначе. После выполнения условия, которое определяется вне этой функции, функция должна заканчиваться. Для меня это звучит довольно стандартно, но у меня такое чувство, что я что-то недопонимаю, и я внедрил его неправильно.

То, что у меня есть прямо сейчас:

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
}

Я надеялся на некоторое руководство, если я подхожу к этому правильно и понимаю, что я делаю неправильно.

Ответ 1

Вы определенно внедрили его неправильно. Вы возвращаете Task<int>, но только после того, как вся работа уже выполнена.

Мне кажется, что вы должны просто иметь синхронный метод:

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

Затем запустите задачу следующим образом:

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

Затем вы можете подождать эту задачу, хотя, хотя в приведенном вами примере нет смысла делать это, так как вы ничего не делаете после await.

Я также соглашусь с Ридом, что использование CancellationToken будет чище, чем статический флаг где-то в другом месте.

Ответ 2

Вы неправильно поняли.

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
}

Весь этот код все еще встречается в вызове intial 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

Вместо того, чтобы пытаться использовать bool для этого, вы должны рассмотреть возможность использования управляемой структуры отмены, встроенной в фреймворк.

В принципе, вы бы построили CancellationTokenSource и передали CancellationToken вашему методу, который можно было бы использовать для отмены отмены.

Наконец, ваш текущий метод никогда не отключится от потока пользовательского интерфейса. Вам нужно будет использовать Task.Run или аналогично, чтобы переместить метод в ThreadPool, если вы не хотите блокировать пользовательский интерфейс.

Ответ 4

В качестве альтернативы вы можете изучить BackgroundWorker класс для выполнения задания, ваш интерфейс будет оставаться интерактивным.