Использование asyn c вместо Task.Run () - PullRequest
3 голосов
/ 19 февраля 2020

У меня есть следующий фрагмент кода:

private void btnAction_Click(object sender, RoutedEventArgs e)
{

    /** Clear the results field */
    txtResult.Text = "";

    /** Disable the button and show waiting status */
    btnAction.IsEnabled = false;
    lblStatus.Text = "Wait...";

    /** Get input from the query field */
    string input = query.Text;

    /** Run a new task */
    Task.Run(() => {

        // calling a method that takes a long time (>3s) to finish and return
        var attempt = someLibrary.doSomethingWith(input);

        // return the result to the GUI thred
        this.Dispatcher.Invoke(() =>
        {

            if (attempt.ContainsKey("success"))
            {
                if (attempt["success"] == true)
                {
                    txtResult.Text = "Success! The door is: " + (attempt["is_open"] ? "open" : "closed");
                    lblStatus.Text = "";
                }
                else
                {
                    lblStatus.Text = "Error! The service says: " + attempt["errorMessage"];
                }
            }

            else
            {
                MessageBox.Show("There was a problem getting results from web service.");
                lblStatus.Text = "";
            }

            /** Re-enable the button */
            btnAction.IsEnabled = true;

        });
    });

}

Теперь я хотел бы:

  • Писать тот же код таким способом, который использует обратный вызов вместо использования Dispatcher.Invoke().
  • Иметь возможность отменить текущее задание, которое вызывает doSomething()
  • Уметь связывать несколько вызовов, т. Е. Ожидать doSomething() и после его завершения doAnotherThing() с помощью результаты предыдущего вызова

Поэтому я хочу написать это, используя модель asyn c.

Что мне делать?

Ответы [ 2 ]

5 голосов
/ 19 февраля 2020

Вы бы пометили свой метод как async, await Task.Run, чтобы продолжения выполнялись на UI , также оставляя только длительный запуск (казалось бы, с привязкой к процессору) задание внутри него

private async void btnAction_Click(object sender, RoutedEventArgs e)
{
   btnAction.IsEnabled = false;
   txtResult.Text = "";       
   lblStatus.Text = "Wait...";

   string input = query.Text;

   // calling a method that takes a long time (>3s) to finish and return
   var attempt =  await Task.Run(() => someLibrary.doSomethingWith(input));

   if (attempt.ContainsKey("success"))
   {
      if (attempt["success"] == true)
      {
         txtResult.Text = "Success! The door is: " + (attempt["is_open"] ? "open" : "closed");
         lblStatus.Text = "";
      }
      else
      {
         lblStatus.Text = "Error! The service says: " + attempt["errorMessage"];
      }
   }  
   else
   {
      MessageBox.Show("There was a problem getting results from web service.");
      lblStatus.Text = "";
   }

   btnAction.IsEnabled = true;

}

Обновление

Чтобы отменить задачу, вы должны использовать CancellationToken из экземпляра am CancellationTokenSource и передайте это в Task.Run, а также в свой долгосрочный метод для проверки на IsCancellationRequested (если можете). Вы отмените вызов, позвонив по номеру CancellationTokenSource.Cancel

Обратите внимание, что вы, вероятно, захотите заключить его в , попытайтесь поймать наконец и поймать OperationCanceledException и поместите свой код включения кнопки в наконец

2 голосов
/ 19 февраля 2020

Модификатор async требует, чтобы функция возвращала Task<T> (или void, и в этом случае любые операторы ожидания будут игнорироваться). Это означает, что использование async и использование Task.Run() - это одно и то же, посылка вашего вопроса не имеет смысла.

Однако, я думаю, вы хотите просто использовать async await синтаксис, чтобы избежать явного вызова Task.Run().

Обратные вызовы

Просто создайте функцию, которая возвращает Task

async Task<T> Foo()

И затем назначьте переменную var bar=await Foo();

Отмена запущенной задачи

Просто используйте CancellationToken

CancellationTokenSource tokenSource = new CancellationTokenSource();

Если вы создаете задачу с двумя аргументами, вторым аргументом является маркер отмены:

var bar= new Task(action, tokenSource.Token)

Это позволяет использовать tokenSource.Cancel ();

Соответствующая ссылка: https://docs.microsoft.com/en-us/dotnet/api/system.threading.cancellationtoken?view=netframework-4.8

Цепочка вызовов

Если вам не требуется определенный порядок выполнения, вы можете использовать Task.WhenAll(), в противном случае вы можете выполнить следующую задачу внутри предыдущей или в коде ожидания.

Task.WhenAll(): https://docs.microsoft.com/en-us/dotnet/api/system.threading.tasks.task.whenall?view=netframework-4.8#System_Threading_Tasks_Task_WhenAll_System_Collections_Generic_IEnumerable_System_Threading_Tasks_Task__

...