2013-08-15 36 views
6

Đầu tiên một chút thông tin cơ bản. Tôi đang trong quá trình tạo mã thư viện C# hiện có phù hợp để thực hiện trên WinRT. Là một phần nhỏ của mã này sâu xuống cần phải làm một tập tin nhỏ IO, đầu tiên chúng tôi đã cố gắng để giữ cho mọi thứ đồng bộ và sử dụng Task.Wait() để ngăn chặn các chủ đề chính cho đến khi tất cả IO đã được thực hiện.Tại sao task.Wait không bế tắc trên một chủ đề không phải là ui? Hay tôi chỉ may mắn?

Chắc chắn, chúng tôi nhanh chóng phát hiện ra dẫn đến bế tắc.

Sau đó tôi thấy mình thay đổi rất nhiều mã trong một mẫu thử nghiệm để làm cho nó "không đồng bộ". Đó là, tôi đã chèn async và chờ đợi từ khóa, và tôi đã thay đổi phương thức trả về kiểu cho phù hợp. Điều này là rất nhiều công việc - quá nhiều công việc vô nghĩa trong thực tế -, nhưng tôi đã có nguyên mẫu làm việc theo cách này.

Sau đó, tôi đã làm một thí nghiệm, và tôi chạy mã gốc với tuyên bố Chờ trên một sợi riêng biệt:

System.Threading.Tasks.Task.Run(()=> Draw(..., cancellationToken) 

Không bế tắc!

Bây giờ tôi đang bối rối nghiêm trọng, bởi vì tôi nghĩ rằng tôi hiểu cách hoạt động của chương trình không đồng bộ. Mã của chúng tôi không (chưa) sử dụng ConfigureAwait (sai) ở tất cả. Vì vậy, tất cả các câu lệnh chờ đợi sẽ tiếp tục trong bối cảnh tương tự như chúng được viện dẫn. Phải không? Tôi giả định điều đó có nghĩa là: cùng một chủ đề. Bây giờ nếu thread này đã gọi "Wait", điều này cũng sẽ dẫn đến một bế tắc. Nhưng nó không.

Bạn có giải thích rõ ràng về đá nào không?

Câu trả lời cho điều này sẽ xác định xem tôi có thực sự làm rối tung mã của chúng tôi hay không bằng cách chèn nhiều từ khóa không đồng bộ/chờ đợi có điều kiện hoặc tôi sẽ giữ nó sạch sẽ và chỉ sử dụng chuỗi. với chỗ ấy. Nếu sự tiếp tục được chạy bởi một chuỗi không bị chặn tùy ý, mọi thứ sẽ ổn. Tuy nhiên, nếu chúng được chạy bởi chuỗi giao diện người dùng, chúng tôi có thể gặp sự cố nếu việc tiếp tục tính toán tốn kém.

Tôi hy vọng rằng vấn đề là rõ ràng. Nếu không, vui lòng cho tôi biết.

Trả lời

7

Tôi có một async/await intro trên blog của tôi, nơi tôi giải thích chính xác những gì bối cảnh là:

Đó là SynchronizationContext.Current, trừ khi đó là null, trong trường hợp này nó là TaskScheduler.Current. Lưu ý: nếu không có TaskScheduler hiện tại, thì TaskScheduler.Current giống với TaskScheduler.Default, đây là công cụ lập lịch tác vụ của nhóm chủ đề.

Trong mã ngày nay, nó thường chỉ đi xuống cho dù bạn có SynchronizationContext hay không; lịch trình tác vụ không được sử dụng nhiều trong ngày hôm nay (nhưng có thể sẽ trở nên phổ biến hơn trong tương lai). Tôi có một article on SynchronizationContext mô tả cách hoạt động và một số triển khai được cung cấp bởi .NET.

WinRT và các khung giao diện người dùng khác (WinForms, WPF, Silverlight) đều cung cấp SynchronizationContext cho chuỗi giao diện người dùng chính của chúng. Ngữ cảnh này chỉ đại diện cho một chuỗi duy nhất, vì vậy nếu bạn trộn lẫn mã không đồng bộ và mã không đồng bộ, bạn có thể nhanh chóng gặp phải các bế tắc. Tôi mô tả lý do tại sao điều này xảy ra chi tiết hơn in a blog post, nhưng tóm lại lý do tại sao nó bị bế tắc là vì phương pháp async đang cố gắng nhập lại SynchronizationContext (trong trường hợp này, tiếp tục thực hiện trên chuỗi giao diện người dùng), nhưng chuỗi giao diện người dùng bị chặn chờ đợi phương thức async hoàn tất.

Nhóm chủ đề không có SynchronizationContext (hoặc TaskScheduler, thông thường). Vì vậy, nếu bạn đang thực hiện trên một thread thread thread và chặn trên mã không đồng bộ, nó sẽ không bế tắc. Điều này là do bối cảnh được capture là bối cảnh pool thread (không gắn với một thread cụ thể), do đó phương thức async có thể nhập lại ngữ cảnh của nó (chỉ bằng cách chạy trên một thread thread thread) trong khi thread thread khác bị khóa chờ đợi để hoàn thành.

Câu trả lời cho điều này sẽ xác định xem tôi có thực sự làm rối tung mã của chúng tôi hay không bằng cách chèn nhiều từ khóa không có điều kiện/chờ đợi hoặc tôi sẽ giữ nó sạch sẽ.) ở đây và ở đó.

Nếu mã của bạn là async thì hoàn toàn không hề lộn xộn chút nào. Tôi không chắc chắn ý bạn là gì bởi "có điều kiện"; Tôi sẽ chỉ làm cho nó tất cả async. await có triển khai "đường dẫn nhanh" để thực hiện đồng bộ hóa nếu thao tác đã hoàn tất.

Chặn trên mã không đồng bộ sử dụng một sợi nền là có thể, nhưng nó có một số hãy cẩn thận:

  1. Bạn không có bối cảnh UI, nên bạn không thể làm được rất nhiều điều UI.
  2. Bạn vẫn phải "đồng bộ hóa" với chuỗi giao diện người dùng và chuỗi giao diện người dùng của bạn không được chặn (ví dụ: nó phải await Task.Run(..), không phải Task.Run(..).Wait()). Điều này đặc biệt đúng đối với các ứng dụng WinRT.
+0

Cảm ơn thông tin. Điều này giải thích khá nhiều. –

+0

Để làm rối tung mọi thứ: đây là mã thư viện hiện có được nhắm mục tiêu ở nhiều nền tảng (.Net), không chỉ WinRT. Chúng tôi muốn sử dụng cùng một mã cho tất cả các nền tảng, vì vậy chúng tôi phải sử dụng #if để tránh chờ/không đồng bộ cho các nền tảng không có sẵn. Ngoài ra, chúng tôi không thể chỉ thay đổi tất cả các API hiện tại thành các phần không đồng bộ, vì điều đó sẽ phá vỡ mọi thứ cho khách hàng hiện tại. Tất cả các câu lệnh #if này làm cho mã hoàn toàn dễ đọc hơn một chút. –

+0

Tôi tìm thấy _idea_ của async/await khá hấp dẫn và hợp lý. Tuy nhiên, việc thực hiện thực tế mà Microsoft cung cấp là rắc rối tôi nghĩ. Thực tế là ngữ nghĩa của các cấu trúc này phụ thuộc vào ngữ cảnh trong đó chuỗi được bắt đầu là một sai lầm mà tôi nghĩ. Chủ yếu là vì ngữ nghĩa của mã chương trình nên phụ thuộc vào ngữ cảnh thời gian chạy càng ít càng tốt (khó có thể giải thích được). Tôi có thể hiểu rằng Microsoft muốn thêm các tùy chọn, nhưng tôi nghĩ rằng việc sử dụng đơn giản chờ đợi và đồng bộ không nên đưa ra những khác biệt như vậy. –

Các vấn đề liên quan