C # Async - как это работает? - PullRequest
47 голосов
/ 29 октября 2010

Microsoft анонсировала Visual Studio Async CTP сегодня (28 октября 2010 г.), который вводит ключевые слова async и await в C # / VB для выполнения асинхронного метода.

Сначала я подумал, что компилятор переводит ключевые слова в создание потока, но согласно белой книге и презентации PDC Андерса Хейлсберга (в 31:00)асинхронная операция полностью происходит в основном потоке.

Как можно параллельно выполнить операцию в том же потоке?Как это технически возможно и на что фактически переведена функция в IL?

Ответы [ 3 ]

81 голосов
/ 29 октября 2010

Работает аналогично ключевому слову yield return в C # 2.0.

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

Это означает, что когда метод запускается, он просто запускает первый шаг, а затем конечный автомат возвращается и планирует некоторую работу, которая будет выполнена - когда работа будет завершена, он будет запускать следующий шаг конечного автомата. Например этот код:

async Task Demo() { 
  var v1 = foo();
  var v2 = await bar();
  more(v1, v2);
}

Будет переведено что-то вроде:

class _Demo {
  int _v1, _v2;
  int _state = 0; 
  Task<int> _await1;
  public void Step() {
    switch(this._state) {
    case 0: 
      this._v1 = foo();
      this._await1 = bar();
      // When the async operation completes, it will call this method
      this._state = 1;
      op.SetContinuation(Step);
    case 1:
      this._v2 = this._await1.Result; // Get the result of the operation
      more(this._v1, this._v2);
  }
}

Важной частью является то, что он просто использует метод SetContinuation, чтобы указать, что после завершения операции он должен снова вызвать метод Step (и метод знает, что он должен запустить второй бит исходного кода, используя поле _state). Вы можете легко представить, что SetContinuation будет что-то вроде btn.Click += Step, который будет работать полностью в одном потоке.

Модель асинхронного программирования в C # очень близка к асинхронным рабочим процессам F # (фактически, это, по сути, одно и то же, за исключением некоторых технических деталей), и написание реактивных однопоточных приложений с графическим интерфейсом с использованием async довольно интересно область - по крайней мере, я так думаю - см., например, эту статью (возможно, мне следует написать версию C # сейчас: -)).

Перевод аналогичен итераторам (и yield return), и фактически было возможно использовать итераторы для реализации асинхронного программирования на C # ранее. Я написал статью об этом некоторое время назад - и я думаю, что она все еще может дать вам некоторое представление о том, как работает перевод.

46 голосов
/ 30 октября 2010

Как можно параллельно выполнить операцию в одном потоке?

Ты не можешь. Асинхронность - это не "параллелизм" или "параллелизм" . Асинхронность может быть реализована с параллелизмом или не может быть. Это может быть реализовано путем разбиения работы на маленькие порции, помещения каждого куска работы в очередь и последующего выполнения каждого куска работы, когда поток ничего не делает.

В моем блоге есть целая серия статей о том, как все это работает; тот, кто имеет непосредственное отношение к этому вопросу, вероятно, поднимется в четверг следующей недели. Часы

http://blogs.msdn.com/b/ericlippert/archive/tags/async/

для деталей.

8 голосов
/ 29 октября 2010

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

Согласно имеющимся документам, есть много деталей, но если я не ошибаюсь, это суть.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...