Như bạn đã khám phá, trong VS11 trình biên dịch sẽ không cho phép một async Main
phương thức. Điều này đã được cho phép (nhưng không bao giờ được khuyến nghị) trong VS2010 với Async CTP.
Tôi có các bài đăng trên blog gần đây về các chương trình bảng điều khiển async / await và không đồng bộ nói riêng. Dưới đây là một số thông tin cơ bản từ bài giới thiệu:
Nếu "chờ đợi" thấy rằng việc chờ đợi chưa hoàn thành, thì nó hoạt động không đồng bộ. Nó cho biết có thể chờ đợi để chạy phần còn lại của phương thức khi hoàn thành và sau đó trả về từ phương thức async. Await cũng sẽ nắm bắt bối cảnh hiện tại khi nó vượt qua phần còn lại của phương thức để chờ đợi.
Sau đó, khi chờ đợi hoàn thành, nó sẽ thực thi phần còn lại của phương thức async (trong bối cảnh đã chụp).
Đây là lý do tại sao đây là một vấn đề trong các chương trình Console với async Main
:
Hãy nhớ từ bài đăng giới thiệu của chúng tôi rằng một phương thức async sẽ trả về với người gọi trước khi hoàn thành. Điều này hoạt động hoàn hảo trong các ứng dụng UI (phương thức chỉ trả về vòng lặp sự kiện UI) và các ứng dụng ASP.NET (phương thức trả về luồng nhưng vẫn giữ yêu cầu sống). Nó không hoạt động tốt cho các chương trình Console: Trả về chính cho HĐH - vì vậy chương trình của bạn sẽ thoát.
Một giải pháp là cung cấp ngữ cảnh của riêng bạn - một "vòng lặp chính" cho chương trình bảng điều khiển tương thích không đồng bộ.
Nếu bạn có máy có Async CTP, bạn có thể sử dụng GeneralThreadAffineContext
từ My Documents \ Microsoft Visual Studio Async CTP \ Samples (C # tests) Kiểm tra đơn vị \ AsyncTestUtilities . Ngoài ra, bạn có thể sử dụng AsyncContext
từ gói NuGet Nito.AsyncEx của tôi .
Đây là một ví dụ sử dụng AsyncContext
; GeneralThreadAffineContext
có cách sử dụng gần như giống hệt nhau:
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();
}
}
Ngoài ra, bạn chỉ có thể chặn luồng Bảng điều khiển chính cho đến khi công việc không đồng bộ của bạn hoàn tất:
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();
}
}
Lưu ý việc sử dụng GetAwaiter().GetResult()
; Điều này tránh việc AggregateException
gói xảy ra nếu bạn sử dụng Wait()
hoặcResult
.
Cập nhật, 2017-11-30: Kể từ Visual Studio 2017 Update 3 (15.3), ngôn ngữ hiện hỗ trợ một async Main
- miễn là nó trả về Task
hoặc Task<T>
. Vì vậy, bây giờ bạn có thể làm điều này:
class Program
{
static async Task Main(string[] args)
{
Bootstrapper bs = new Bootstrapper();
var list = await bs.GetList();
}
}
Các ngữ nghĩa dường như giống như GetAwaiter().GetResult()
phong cách chặn luồng chính. Tuy nhiên, chưa có thông số ngôn ngữ nào cho C # 7.1, vì vậy đây chỉ là giả định.