Як ви виявили, у 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 ще немає мовних специфікацій, тому це лише припущення.