Как вы обнаружили, в VS11 компилятор запрещает async Main
метод. Это было разрешено (но никогда не рекомендуется) в VS2010 с Async CTP.
У меня есть последние сообщения в блоге об асинхронных / ожидающих и , в частности, асинхронных консольных программах . Вот некоторая справочная информация из вступительного поста:
Если «ожидающий» видит, что ожидаемое еще не завершено, он действует асинхронно. Он сообщает ожидающему выполнения оставшуюся часть метода после его завершения, а затем возвращается из асинхронного метода. Await также будет захватывать текущий контекст, когда он передает оставшуюся часть метода ожидающему.
Позже, когда ожидаемое завершится, оно выполнит оставшуюся часть асинхронного метода (в захваченном контексте).
Вот почему это проблема в консольных программах с async Main
:
Помните из нашего вступительного поста, что асинхронный метод вернется к своему вызывающему до завершения. Это прекрасно работает в приложениях пользовательского интерфейса (метод просто возвращается в цикл событий пользовательского интерфейса) и приложениях ASP.NET (метод возвращает поток, но поддерживает запрос в действии). Это не очень хорошо работает с консольными программами: Main возвращается в ОС, поэтому ваша программа завершает работу.
Одним из решений является предоставление собственного контекста - «основного цикла» для вашей консольной программы, который является асинхронным.
Если у вас есть машина с Async CTP, вы можете использовать GeneralThreadAffineContext
из Моих документов \ Microsoft Visual Studio Async CTP \ Samples (C # Testing) Unit Testing \ AsyncTestUtilities . Кроме того, вы можете использовать AsyncContext
из моего пакета 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, обновление 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, так что это только предположение.