Как вы обнаружили, в VS11 компилятор запрещает метод async Main
.Это было разрешено (но никогда не рекомендуется) в VS2010 с Async CTP.
У меня есть последние сообщения в блоге о async / await и асинхронных консольных программах , в частности.Вот некоторая справочная информация из вступительного поста:
Если «ожидание» видит, что ожидаемое еще не завершено, то оно действует асинхронно.Он сообщает ожидающему выполнения оставшуюся часть метода после его завершения, а затем возвращает из асинхронного метода.Await также будет захватывать текущий контекст , когда он передает оставшуюся часть метода ожидающему.
Позже, когда ожидаемое завершится, он выполнит оставшуюся часть асинхронного метода (в пределахзахваченный контекст).
Вот почему это проблема в консольных программах с async Main
:
Помните из нашего вступительного поста, что асинхронный метод будет верните вызывающему абоненту до его завершения.Это прекрасно работает в приложениях пользовательского интерфейса (метод просто возвращается в цикл событий пользовательского интерфейса) и приложениях ASP.NET (метод возвращает поток, но поддерживает запрос в действии).Для консольных программ это не очень хорошо работает: Main возвращается в ОС - поэтому ваша программа завершает работу.
Одним из решений является предоставление собственного контекста - «основного цикла» для вашей консольной программы.это асинхронно.
Если у вас есть машина с Async CTP, вы можете использовать GeneralThreadAffineContext
из Мои документы \ Microsoft Visual Studio Async CTP \ Samples (C # Testing) Unit Testing \ AsyncTestUtilities.Кроме того, вы можете использовать AsyncContext
из my Nito.AsyncEx в пакете NuGet .
Вот пример использования AsyncContext
;GeneralThreadAffineContext
имеет почти идентичное использование:
using Nito.AsyncEx;
class Program
{
static void Main(string[] args)
{
AsyncContext.Run(() => MainAsync(args));
}
static async void MainAsync(string[] args)
{
Bootstrapper bs = new Bootstrapper();
var list = await bs.GetList();
}
}
В качестве альтернативы, вы можете просто заблокировать основной поток консоли, пока ваша асинхронная работа не будет завершена:
class Program
{
static void Main(string[] args)
{
MainAsync(args).GetAwaiter().GetResult();
}
static async Task MainAsync(string[] args)
{
Bootstrapper bs = new Bootstrapper();
var list = await bs.GetList();
}
}
Обратите внимание на использование GetAwaiter().GetResult()
;это позволяет избежать переноса AggregateException
, который происходит, если вы используете Wait()
или Result
.
Обновление, 2017-11-30: Начиная с Visual Studio 2017 Update 3 (15.3), язык теперь поддерживает async Main
- до тех пор, пока он возвращает Task
или Task<T>
.Теперь вы можете сделать это:
class Program
{
static async Task Main(string[] args)
{
Bootstrapper bs = new Bootstrapper();
var list = await bs.GetList();
}
}
Семантика выглядит так же, как и стиль GetAwaiter().GetResult()
блокировки основного потока.Однако для C # 7.1 спецификации языка пока нет, так что это только предположение.