Вопрос

Я не вижу разные между C # 'S (и VB's) новые асинхронизации и .NET 4.0 Задача параллельной библиотеки. Отказ Взять, например, код Эрика ЛПППППТ отсюда:

async void ArchiveDocuments(List<Url> urls) {
    Task archive = null;
    for(int i = 0; i < urls.Count; ++i) {
        var document = await FetchAsync(urls[i]);
        if (archive != null)
            await archive;
        archive = ArchiveAsync(document);
    }
}

Кажется, что await Ключевое слово обслуживает два разных целя. Первое вхождение (FetchAsync) кажется, что означает, «Если это значение используется позже в методе И его задача не закончена, подожди, пока она не завершится, прежде чем продолжить. Второй экземпляр (archive) кажется, что означает, «Если эта задача еще не закончена, подожди Сейчас пока он не завершится. " Если я ошибаюсь, пожалуйста, поправьте меня.

Разве это не мог так легко написать так?

void ArchiveDocuments(List<Url> urls) {
    for(int i = 0; i < urls.Count; ++i) {
        var document = FetchAsync(urls[i]);       // removed await
        if (archive != null)
            archive.Wait();                       // changed to .Wait()
        archive = ArchiveAsync(document.Result);  // added .Result
    }
}

Я заменил первый await с А. Task.Result где значение на самом деле необходимо, а второе await с Task.Wait(), где ожидание на самом деле происходит. Функциональность есть (1) уже реализован, а (2) гораздо ближе семантически к тому, что на самом деле происходит в коде.

Я понимаю, что async Метод переписан как государственный станок, похожий на итераторы, но я также не вижу, какие выгоды, которые приносят. Любой код, который требует работы другого потока (например, загрузка), все равно потребуется другой поток, и любой код, который не (например, чтение из файла), все равно может использовать TPL для работы только с одним потоком.

Я, очевидно, не хватает что-то огромное здесь; Кто-нибудь может помочь мне понять это немного лучше?

Это было полезно?

Решение

Я думаю, что недоразумение возникает здесь:

Похоже, что ключевое слово await обслуживает два разных целя. Первое вхождение (FetchAsync), кажется, означает, что «если это значение используется позже в способе, и его задача не закончится, подожди, пока она не завершится, прежде чем продолжить». Второй экземпляр (архив), похоже, означает, что «если эта задача еще не закончена, подожди сейчас, пока она не завершится». Если я ошибаюсь, пожалуйста, поправьте меня.

Это на самом деле совершенно неверно. Оба из них имеют одинаковое значение.

В вашем первом случае:

var document = await FetchAsync(urls[i]);

Что здесь происходит, состоит в том, что время выполнения гласит «Начните звонить FetchAsync, затем вернуть текущую точку выполнения на резьбу, вызывающую этот метод». Здесь здесь нет «ожидания» - вместо этого исполнение возвращается к контексту вызывающего синхронизации, и вещи продолжают проливать. В какой-то момент в будущем задача FetchAsync завершится, и в этот момент этот код возобновится в контексте синхронизации звонков потока, а следующее утверждение произойдет (присвоение переменной документа).

Затем выполнение будет продолжаться до тех пор, пока второй не ожидает вызова - в какое время произойдет то же самое - если Task<T> (Архив) не завершен, выполнение будет выпущено к контексту вызова - в противном случае будет установлен архив.

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

Другие советы

Это огромная разница:

Wait() блоки, await не блокирует. Если вы запустите Async версию ArchiveDocuments() На вашу битую GUI графический интерфейс останется отзывчивым, пока запущены операции по выборам и архивированию. Если вы используете версию TPL с Wait(), Ваш графический интерфейс будет заблокирован.

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

Андерс варил его до очень краткого ответа в канале 9 в прямом эфире, который он сделал. Я настоятельно рекомендую это

Новые ключевые слова Async и await позволяют вам Оршестретивное совпадение в ваших приложениях. Они на самом деле не вводят какое-либо параллелизм в вашей заявке.

TPL и более конкретно задача в одну сторону Вы можете использовать, чтобы на самом деле выполнять операции одновременно. Новое ключевое слово Async и await позволит вам составить Эти параллельные операции в «синхронном» или «линейном» моде.

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

Возможность превратить поток программы управления в государственный автомат - это то, что делает эти новые ключевые слова, интересные. Думать об этом как Урожайность контроля, а не значения.

Проверить Этот канал 9 видео Андерс говорил о новой особенности.

Проблема вот что подпись ArchiveDocuments вводит в заблуждение. Это имеет явное возвращение void Но на самом деле возвращение Task. Отказ Для меня пустота подразумевает синхронный, так как нет никакого способа «подождать», чтобы закончить. Рассмотрим альтернативную подпись функции.

async Task ArchiveDocuments(List<Url> urls) { 
  ...
}

Для меня, когда это написано таким образом, разница гораздо очевидна. То ArchiveDocuments Функция не то, что завершает синхронно, но закончится позже.

Призыв к FetchAsync() все еще блокирует, пока он не завершит (если не указано в звонках await?) Ключ в том, что элемент управления возвращается в абонент (потому что ArchiveDocuments Сам метод объявлен как async). Таким образом, вызывающий абонент может быть счастливо продолжать обработку пользовательской логики, отвечать на события и т. Д.

Когда FetchAsync() завершает, он прерывает абонент, чтобы закончить петлю. Это хиты ArchiveAsync() и блоки, но ArchiveAsync() Вероятно, просто создает новую задачу, начнет ее и возвращает задачу. Это позволяет начать вторую петлю, а задача обрабатывается.

Вторая петля хитов FetchAsync() и блоки, возвращая контроль к абонеру. Когда FetchAsync() завершает, это снова прерывает абонент продолжить обработку. Затем это хиты await archive, который возвращает контроль к абонеру до Task Создано в Loop 1 завершает. Как только эта задача завершена, вызывающий абонент снова прерывается, а вторая циклические вызовы ArchiveAsync(), что получает начальную задачу и начинает петлю 3, повторить ad naiseum..

Ключ возвращает элемент управления звонящему, в то время как тяжелые подъемники выполняются.

Ключевое слово await не вводит параллелизм. Это как ключевое слово выхода, он говорит компилятору реструктурировать свой код в Lambda, управляемый состоянием.

Чтобы увидеть, как не будет выглядеть код без «ждут», см. Это отличная ссылка: http://blogs.msdn.com/b/windowsappdev/archive/2012/04/24/diving-deep-with-winrt-andawait.aspx.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top