Вызов метода async с С# 5.0

Я делаю некоторые тесты с новым асинхронным шаблоном С# 5.0 (async/await) У меня проблема с пониманием того, как вызываются асинхронные методы.

Учитывая этот код:

private async Task<string> DownloadAsync()
    {
        progress.ProgressChanged += (s, e) =>
            {
                progressBar1.Value = e.value;
            };

            return await DownloadSomething(myurl, progress);

    }

private async void CallDownloadAsync()
    {
        string text = await DownloadAsync();
        progressBar1.Value = 0;
        label1.Text = "Done!";
    }

private void button4_Click(object sender, EventArgs e)
    {
        CallDownloadAsync();
    }

Итак, этот код работает очень хорошо. Когда я нажимаю кнопку "button4", начинается загрузка, и мой ProgressBar обновляется правильно.

Но я хотел бы немного уменьшить свой код, удалив метод CallDownloadAsync() следующим образом:

private void button4_Click(object sender, EventArgs e)
    {
        new Action(async () =>
        {
            string result = await Task.Run<string>(() => DownloadAsync());
        }).Invoke();
        label1.Text = "Running...";
    }

Итак, здесь я хочу, чтобы прямо инициировать действие, которое вызывает метод DownloadAsync, но когда я нажимаю на свою Button4, у меня есть операция Неверная операция на основе progressBar. Поэтому я не понимаю, в чем основное отличие между Action() и вызовом моего метода CallDownloadAsync().

Ответ 1

Разница в том, что в первом случае вы вызываете CallDownloadAsync() из потока пользовательского интерфейса (контекст).

В последнем случае DownloadAsync() вызывается из инициированной задачи, которая обычно выполняется в другом потоке, создаваемом TPL (параллельной библиотекой задач) из потока пользовательского интерфейса или потоков, созданных из него.

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

Ответ 2

Вы можете найти мое async/await intro. В частности, метод async не работает в фоновом потоке; Task.Run используется для запуска чего-то в фоновом потоке, следовательно, разница в вашем коде.

В общем, вам следует избегать async void, если вы не пишете обработчик событий async. Вот так:

private async void button4_Click(object sender, EventArgs e)
{
    label1.Text = "Running...";
    string result = await DownloadAsync();
    progressBar1.Value = 0;
    label1.Text = "Done!";
}