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

может кто-нибудь объяснить, что

5 ответов


они просто говорили об этом в PDC вчера!

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

//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();

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

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

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

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


принятый в настоящее время ответ вводит в заблуждение. 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
Выход Из DoWork ()


для тех, кто новичок в асинхронном программировании в .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 и подключил его к обратному вызову, поэтому во время выполнения второй пример будет вести себя так же, как первый.

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


Если бы мне пришлось реализовать его на 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");
        }
    }
}