Что делает новая функция C # await? [закрыто]

83

Может ли кто-нибудь объяснить, что awaitделает функция?

Крис Никол
источник
Хорошие примеры также на dotnetperls.com/async .
Miljen Mikic
Я не считаю, что этот вопрос слишком широкий и его следует закрывать. Он спрашивает, что означает одно ключевое слово. (
Более

Ответы:

62

Они только вчера говорили об этом на PDC !

Await используется вместе с задачами (параллельное программирование) в .NET. Это ключевое слово будет введено в следующей версии .NET. Это более или менее позволяет вам «приостановить» выполнение метода, чтобы дождаться завершения выполнения Задачи. Вот краткий пример:

//create and run a new task  
Task<DataTable> dataTask = new Task<DataTable>(SomeCrazyDatabaseOperation);

//run some other code immediately after this task is started and running  
ShowLoaderControl();  
StartStoryboard();

//this will actually "pause" the code execution until the task completes.  It doesn't lock the thread, but rather waits for the result, similar to an async callback  
// please so also note, that the task needs to be started before it can be awaited. Otherwise it will never return
dataTask.Start();
DataTable table = await dataTask;

//Now we can perform operations on the Task result, as if we're executing code after the async operation completed  
listBoxControl.DataContext = table;  
StopStoryboard();  
HideLoaderControl();
RTigger
источник
2
Когда это обещания в форме C #: en.wikipedia.org/wiki/Futures_and_promises
12
Похоже на Thread.Join ().
Стив Гуиди,
10
Напоминает мне COMEFROM
Джоэл Спольски
20
Для полноты картины добавим, что приведенный выше фрагмент кода должен быть заключен в метод, украшенный ключевым словом async. Этот метод должен немедленно вернуться, как только в нем встретится первое ключевое слово await.
Przemek
14
Проще говоря: он позволяет «приостановить» метод, но следует отметить, что он не приостанавливает и не блокирует поток.
Мэтт Кринкло-Фогт,
47

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

Думайте об этом как о чем-то похожем на yield returnоператор в методе, создающем IEnumerable. Когда среда выполнения достигает yield, она в основном сохраняет текущее состояние метода и возвращает полученное значение или ссылку. В следующий раз, когда IEnumerator.MoveNext () вызывается для возвращаемого объекта (который создается внутри средой выполнения), старое состояние метода восстанавливается в стеке, и выполнение продолжается со следующей строки после, yield returnкак если бы мы никогда не покидали метод. Без этого ключевого слова тип IEnumerator должен быть настроен для хранения состояния и обработки итерационных запросов с помощью методов, которые могут действительно стать ОЧЕНЬ сложными.

Точно так же метод, помеченный как, asyncдолжен иметь хотя бы один await. Во awaitвремя выполнения среда выполнения сохранит состояние текущего потока и стек вызовов, выполнит асинхронный вызов и вернется к циклу обработки сообщений среды выполнения, чтобы обработать следующее сообщение и сохранить реагирование приложения. Когда асинхронная операция завершена, при следующей возможности планирования стек вызовов для выполнения асинхронной операции возвращается и продолжается, как если бы вызов был синхронным.

Итак, эти два новых ключевых слова в основном упрощают кодирование асинхронных процессов, как и yield returnупрощают создание настраиваемых перечислимых элементов. Имея пару ключевых слов и немного базовых знаний, вы можете пропустить все запутанные и часто подверженные ошибкам детали традиционного асинхронного шаблона. Это будет НЕОБХОДИМ практически в любом приложении с графическим интерфейсом, управляемом событиями, например Winforms, WPF или Silverlight.

KeithS
источник
31

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

Вот иллюстрация:

internal class Program
{
    private static void Main(string[] args)
    {
        var task = DoWork();
        Console.WriteLine("Task status: " + task.Status);
        Console.WriteLine("Waiting for ENTER");
        Console.ReadLine();
    }

    private static async Task DoWork()
    {
        Console.WriteLine("Entered DoWork(). Sleeping 3");
        // imitating time consuming code
        // in a real-world app this should be inside task, 
        // so method returns fast
        Thread.Sleep(3000);

        await Task.Run(() =>
            {
                for (int i = 0; i < 10; i++)
                {
                    Console.WriteLine("async task iteration " + i);
                    // imitating time consuming code
                    Thread.Sleep(1000);
                }
            });

        Console.WriteLine("Exiting DoWork()");
    }
}

Вывод:

Вступил в DoWork (). Спящий режим 3
итерация асинхронной задачи 0
Статус задачи: WaitingForActivation
Ожидание ENTER
итерация асинхронной задачи 1 итерация
асинхронной задачи 2 итерация
асинхронной задачи 3 итерация
асинхронной задачи 4 итерация
асинхронной задачи 5 итерация
асинхронной задачи 6 итерация
асинхронной задачи 7 итерация
асинхронной задачи 8 итерация
асинхронной задачи 9
Выход Выполнять работу()

Анри
источник
1
Вы также знаете, что он будет блокировать вызывающего абонента на 3 секунды, прежде чем он даже даст им задачу, которую они могут выполнить await? Значит, если это вызывается из потока пользовательского интерфейса, он блокирует поток пользовательского интерфейса на 3 секунды? Идея этой модели - избегать подобных вещей.
Servy
1
@ Серви, да, вот в чем дело. Показать все этапы исполнения. Это не реальный пример.
Анри
7
@ Слуга, ты меня троллишь?
Анри
2
Нет. Я пытаюсь помочь вам улучшить ваш ответ.
сервировка
2
@ Анри ... Я очень ценю твои усилия. Большое спасибо!!
Правин Праджапати
11

Для всех, кто плохо знаком с асинхронным программированием в .NET, вот (полностью поддельная) аналогия в сценарии, с которым вы, возможно, более знакомы, - вызовы AJAX с использованием JavaScript / jQuery. Простой пост jQuery AJAX выглядит так:

$.post(url, values, function(data) {
  // AJAX call completed, do something with returned data here
});

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

Теперь, если JavaScript поддерживает await ключевое слово (чего, конечно, нет ( пока! )), Вы можете добиться того же с помощью этого:

var data = await $.post(url, values);
// AJAX call completed, do something with returned data here

Это намного чище, но похоже, что мы ввели синхронный блокирующий код. Но (поддельный) компилятор JavaScript взял бы все, что было после, awaitи связал бы это с обратным вызовом, поэтому во время выполнения второй пример будет вести себя так же, как первый.

Может показаться, что это не экономит много работы, но когда дело доходит до таких вещей, как обработка исключений и контексты синхронизации, компилятор фактически делает за вас много тяжелой работы. Для получения дополнительной информации я бы порекомендовал ответы на часто задаваемые вопросы, за которыми следует серия блогов Стивена Клири .

Тодд Менье
источник
Если придерживаться этой фальшивой аналогии (которая, кстати, мне очень помогла, так что спасибо!), Что вы имеете в виду «все после»? Только все, что находится в рамках одной и той же функции (метода)? Или все, что после него, как и все, что можно было добавить в стек вызовов?
2
«Все после» = остальная часть метода. Компилятор эффективно перезаписывает остальную часть метода в виде обратного вызова, и управление немедленно возвращается вызывающей стороне текущего метода.
Todd Menier
1
Великолепный Тодд, еще раз спасибо за ваше объяснение. Я уверен, что это полезно и для других.
-2

Если бы мне пришлось реализовать это на Java, это выглядело бы примерно так:

/**
 * @author Ilya Gazman
 */
public abstract class SynchronizedTask{

    private ArrayList<Runnable> listeners = new ArrayList<Runnable>();

    private static final ThreadPoolExecutor threadPoolExecutor =  new ThreadPoolExecutor(6, 6, 0, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<Runnable>(1000));

    public final void await(Runnable listener){
        synchronized (this) {
            listeners.add(listener);
        }
    }

    public void excecute(){
        onExcecute();
        for (int i = listeners.size() - 1; i >= 0; i--) {
            Runnable runnable;
            synchronized (this) {
                runnable = listeners.remove(i);
            }
            threadPoolExecutor.execute(runnable);
        }
    }

    protected abstract void onExcecute();
}

Ваше приложение будет использовать его так:

public class Test{
    private Job job = new Job();

    public Test() {
        craeteSomeJobToRunInBackground();
        methode1();
        methode2();
    }

    private void methode1(){
        System.out.println("Running methode 1");
        job.await(new Runnable() {

            @Override
            public void run() {
                System.out.println("Continue to running methode 1");
            }
        });
    }

    private void methode2(){
        System.out.println("Running methode 2");
    }

    private void craeteSomeJobToRunInBackground() {
        new Thread(new Runnable() {

            @Override
            public void run() {
                job.excecute();
            }
        }).start();
    }

    private class Job extends SynchronizedTask{

        @Override
        protected void onExcecute() {
            try {
                Thread.sleep(1000);
            }
            catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("Job is done");
        }
    }
}
Илья Газман
источник