Async Framework TaskEx.Проблема с задержкой

#c# #frameworks #asynchronous

#c# #фреймворки #асинхронный

Вопрос:

Я обнаружил какую-то проблему при вызове TaskEx.Задержка с помощью CTP Async Framework в C#

 public async Task<string> TestAsync() {
    return await TaskEx.RunEx<string>( async () => { //in real app this is time consuming code so this line is so complex 
            await TaskEx.Delay( 1000 );
            return "test;
        } );
}

public async void Test {
    var count = 0;
    while( count < 100 ) {
        var val = await TestAsync();
        Console.WriteLine( val ); // in real app this line adds elements to observable collection binded to ListBox
        count  ;
    }
}
  

Вывод на консоль появляется в основном один раз (иногда два или три раза), но не 100 раз, как я ожидал.

Редактировать:

Это не консольное приложение (приведенный выше код упрощен), это приложение WP7. Без использования задержки все работает нормально.

Комментарии:

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

2. Если вы считаете, что с TaskEx что-то не так. Задержка, затем попробуйте отобразить ее в консольном приложении.

3. Я почти уверен — откройте образец проекта (C # Windows Phone) Netflix из пакета Microsoft Visual Studio Async CTP. Добавить ожидающую задачу. Задержка (1000) в LoadMoviesAsync в MainPage.xaml.cs около 78 строки прямо под imageCount = фильмы. Длина; и измените pageCount на 2, чтобы лучше видеть результаты. Запустите приложение, введите 2000 в поле год и перемещайте элементы в списке вверх и вниз. Через некоторое время элементы перестанут загружаться, но их все еще сотни. Удалите задержку, и проблемы не будет!

Ответ №1:

Это потому, что метод Test действительно является асинхронным методом. Компилятор C # просто позволяет скрыть это, используя void возвращаемый тип. Итак, если ваш основной метод выглядит следующим образом

 public static void Main() {
  Test();
}
  

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

Ответ №2:

Вероятно, это связано с тем, что вы работаете в консольном приложении, а не в приложении Windows.

В консольном приложении Async ведет себя иначе, чем в приложениях Windows Forms, WPF или WCF, поскольку отсутствует контекст синхронизации. Таким образом, ожидание отправляется обратно в поток threadpool и фактически не «ожидает» выполнения.

Если вы запустите это в приложении WPF или Windows Forms, оно будет вести себя так, как ожидалось.


Кстати, ваш метод слишком сложен. Вы можете просто сделать:

 public async Task<string> TestAsync() {
    await TaskEx.Delay( 1000 );
    return "test";
}
  

По сути, это будет работать так же, как ваш предыдущий метод, но с гораздо меньшими накладными расходами (и намного проще).

Комментарии:

1. Я думаю, что утверждение «асинхронность не работает должным образом в консольном приложении» вводит в заблуждение, потому что это явно зависит от ваших ожиданий. Асинхронность работает именно так, как я ожидаю от нее в консольном приложении, где я провожу большую часть своего тестирования 🙂 Пример кода в настоящее время неполон, но его можно было бы легко запустить разумным способом в консольном приложении.

2. @Jon: Поведение Async и await в приложении консольного режима настолько сильно отличается от поведения в приложениях Windows Forms, WPF, WCF и т.д. (Если, конечно, вы не устанавливаете пользовательский контекст синхронизации). Обычное руководство тем, что происходит, резко меняется, поскольку возникает синхронизация потоков и множество проблем. Да, вполне возможно написать разумное приложение консольного режима с использованием async, но это требует от вас действительно уделять внимание тому, что вы делаете.

3. Да. На самом деле это довольно легко сделать в консольном приложении — все, что вам нужно сделать, это либо ждать вечно (пока вам не надоест), либо дождаться задачи, возвращаемой из асинхронного метода. Но да, это другое. Я не смотрел, как ведет себя служба WCF — я бы ожидал , что она будет потокоустойчивой, то есть использует пул потоков, — но я не тестировал это. Конечно, было бы неплохо иметь возможность писать серверный код, которому было бы все равно, в каком потоке выполняется каждый шаг.

4. @Jon: WCF устанавливает контекст синхронизации в вызов, поэтому он ведет себя гораздо более похоже на Windows forms. По умолчанию каждый вызов службы выполняется в своем собственном потоке (со своим собственным контекстом), так что это работает довольно хорошо, и вам не нужно беспокоиться о синхронизации потоков. Если вы измените режим параллелизма службы, это может быть другим, но это поведение по умолчанию.