Як ви виявили, у VS11 компілятор забороняє async Mainметод. Це було дозволено (але ніколи не рекомендується) у VS2010 за допомогою CTP Async.
У мене є останні повідомлення в блозі про програми асинхронізації / очікування та зокрема асинхронних консольних програм . Ось основна інформація з вступу:
Якщо "в очікуванні" бачить, що очікуване не завершилося, то воно діє асинхронно. Він повідомляє очікуваному запустити залишок методу після його завершення, а потім повертається з методу async. Очікування також захопить поточний контекст, коли він передасть залишок методу очікуваному.
Пізніше, коли очікуване завершиться, воно виконає решту методу асинхронізації (у захопленому контексті).
Ось чому це проблема в консольних програмах із async Main:
Згадайте з нашого вступного допису, що метод асинхронізації повернеться до свого виклику до його завершення. Це прекрасно працює в програмах UI (метод просто повертається до циклу подій UI) та додатках ASP.NET (метод повертається з потоку, але підтримує запит живим). Це не так добре виходить для консольних програм: Main повертається до ОС - значить, програма виходить з програми.
Одне рішення - забезпечити власний контекст - "основний цикл" для вашої консольної програми, сумісний з асинхронністю.
Якщо у вас є машина з CTP Async, ви можете використовувати GeneralThreadAffineContextв розділі Мої документи \ Microsoft Visual Studio Async CTP \ Samples (тестування C #) Тестування блоку \ 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 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 ще немає мовних специфікацій, тому це лише припущення.