Ожидание ключевых слов

Итак, у меня есть следующий код

private async void button1_Click(object sender, EventArgs e)
{
    await DoSomethingAsync();

    MessageBox.Show("Test");
}

private async Task DoSomethingAsync()
{
    for (int i = 0; i < 1000000000; i++)
    {
        int a = 5;
    }; // simulate job

    MessageBox.Show("DoSomethingAsync is done");

    await DoSomething2Async();
}

private async Task DoSomething2Async()
{
    for (int i = 0; i < 1000000000; i++)
    {
        int a = 5;
    } // simulate job

    MessageBox.Show("DoSomething2Async is done");
}

Пока оба MessageBoxes не показаны, основной поток является блоком (я имею в виду, что само приложение заморожено). Очевидно, что-то не так с моим кодом, и я не могу понять, что. Я никогда раньше не использовал async/await. это моя первая попытка.

EDIT:

Фактически, что я хочу сделать, это запустить асинхронное выполнение DoSomethingAsync, чтобы при нажатии кнопки MessageBox.Show("Test"); выполнялся, даже если DoSomethingAsync является неполным.

Ответ 1

Я думаю, вы неправильно поняли, что такое асинхронное средство. Это не значит, что метод работает в другом потоке!

Асинхронный метод выполняется синхронно до первого await, а затем возвращает Task вызывающему (если только он не async void, то он ничего не возвращает). Когда ожидаемая задача завершается, выполнение возобновляется после await, как правило, в том же потоке (если у него есть SynchronizationContext).

В вашем случае Thread.Sleep находится перед первым ожиданием, поэтому он выполняется синхронно, прежде чем управление возвращается вызывающему. Но даже если это было после await, он все равно блокирует поток пользовательского интерфейса, если только вы не настроили awaiter не на захват контекста синхронизации (используя ConfigureAwait(false)).

Thread.Sleep - метод блокировки. Если вы хотите использовать асинхронный эквивалент, используйте await Task.Delay(3000), как указано в ответе Шрирама Сактивеля. Он немедленно вернется и возобновится через 3 секунды, не блокируя поток пользовательского интерфейса.

Это распространенное заблуждение, что async связан с многопоточным. Это может быть, но во многих случаях это не так. Новый поток неявно порождается только потому, что метод async; для создания новой нити, это должно быть сделано явно в какой-то момент. Если вы специально хотите, чтобы метод работал в другом потоке, используйте Task.Run.

Ответ 2

Вы должны заметить, что методы, отмеченные async, больше не будут вести себя async, если ему не хватает await. У вас было бы предупреждение о компиляторе.

Предупреждение 1 В этом асинхронном методе отсутствуют операторы "ждут" и будут выполняться синхронно. Рассмотрите возможность использования оператора ожидания неблокирующие вызовы API или "ждут Task.Run(...)" для работы с ЦП на фоновом потоке.

Вы должны обратить внимание на эти предупреждения.

Сделайте это асинхронно. Когда я говорю асинхронно, это действительно асинхронное несинхронное задание в другом потоке. Используйте Task.Delay, который действительно асинхронен. Используйте Task.Run для некоторого трудоемкого задания, которое связано с ЦП.

private async void button1_Click(object sender, EventArgs e)
{
   await DoSomethingAsync();
}

private async Task DoSomethingAsync()
{
    await Task.Delay(3000); // simulate job

    MessageBox.Show("DoSomethingAsync is done");

    await DoSomething2Async();
}

private async Task DoSomething2Async()
{
    await Task.Delay(3000); // simulate job

    MessageBox.Show("DoSomething2Async is done");
}

Ответ 3

Вы ничего не делаете асинхронно. Попробуйте:

await Task.Run(() => Thread.Sleep(3000))

Когда вы ожидаете метода, то, что вы эффективно делаете, это обеспечение обратного вызова с кодом, который следует за ним, как то, что выполняется после его завершения (в предыдущих воплощениях async вам действительно нужно было установить это самостоятельно). Если вы не await ничего, то метод на самом деле не является асинхронным.

Для получения дополнительной информации вы можете сделать хуже, чем здесь:

http://msmvps.com/blogs/jon_skeet/archive/2011/05/08/eduasync-part-1-introduction.aspx

После редактирования; попробуйте следующее:

public void MyMessageBox()
{
  var thread = new Thread(() =>
  {
      MessageBox.Show(...);
  });
  thread.Start();
}

Хотя, вы уверены, что это действительно окно сообщения, которое вы хотите? Я бы подумал, что обновление строки состояния/индикатора выполнения улучшится.

Ответ 4

В DoSomethingAsync Thread.Sleep вызывается до await, а в DoSomething2Async не выполняется асинхронная задача.