Метод C# Progress выполняется после возврата await

Я пытаюсь асинхронное ожидание в первый раз и сталкиваюсь с проблемой с использованием Progress. Я вызываю свой асинхронный метод, используя await и pass в моем прогрессе. Я хотел бы, чтобы прогресс отображался, и когда метод заканчивает сообщение, что мы "Готово!" отображается. Что происходит, это то, что прогресс отображается "Готово!" а затем отображается последнее сообщение о ходе выполнения, это пример вывода: 1, 2,

Готово! 3

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

 public async void button1_Click(object sender, EventArgs e)
    {
        await JustDoItAsync(new Progress<int>(ProgressUpdate));

        textBox1.Text += "\r\nDone!\r\n";
    }

    public async Task JustDoItAsync(IProgress<int> progress)
    {
        for (int i = 0; i < 3; i++)
        {
            await Task.Delay(500);
            progress.Report(i + 1);
        }
    }

    private void ProgressUpdate(int step)
    {
        textBox1.Text += step + "\r\n";
    }

1 ответ

Решение

К сожалению, такое поведение не является неожиданным. Обновления прогресса ставятся в очередь и могут поступить после завершения работы. С другой стороны, завершение задачи является синхронным (при условии совместимых контекстов).

В этом случае, так как вы используете IProgress<T>тогда ваш JustDoItAsync никогда не должен получать прямой доступ к интерфейсу. Таким образом, вы должны быть в состоянии использовать ConfigureAwait(false) на ваше awaitв этом методе, и это должно позаботиться об этом.

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

public async void button1_Click(object sender, EventArgs e)
{
  Action<int> progressUpdate = ProgressUpdate;
  await JustDoItAsync(new Progress<int>(update => progressUpdate?.Invoke(update)));
  progressUpdate = null;
  textBox1.Text += "\r\nDone!\r\n";
}

Другой вариант - встроить флаг прямо в ваш IProgress<T> реализация.

Другие вопросы по тегам