2012-06-11 48 views
7

Tôi có thư viện C# muốn có khả năng Gửi/Đăng làm việc với chuỗi chủ đề "chính" (nếu có). thư viện này có thể được sử dụng bởi:Chụp chủ đề chính SynchronizationContext hoặc Dispatcher từ thư viện

  • Một ứng dụng winforms
  • Một ứng dụng bản địa (với giao diện người dùng)
  • Một ứng dụng giao diện điều khiển (không có giao diện người dùng)

Trong thư viện tôi muốn muốn capture cái gì đó (A SynchronizationContext, Dispatcher, Task Scheduler, hoặc cái gì khác) trong quá trình khởi tạo, nó sẽ cho phép tôi (sau này) Send/Post làm việc với thread chính (nếu thread chính có khả năng đó) -ie nó có một máy bơm tin nhắn). Ví dụ, thư viện muốn tạo một số giao diện người dùng Winforms trên luồng chính nếu và chỉ khi ứng dụng chính có khả năng cho tôi để có được chủ đề chính.

Những điều tôi đã cố gắng:

  1. Một SynchronizationContext:. Chụp làm việc này tốt cho một ứng dụng Winforms (một WindowsFormsSynchronizationContext sẽ được cài đặt như các Current SynchronizationContext này cũng hoạt động tốt cho các ứng dụng giao diện điều khiển - kể từ Tôi có thể phát hiện ra rằng SynchronizationContext hiện tại là null (và do đó, biết rằng tôi không có khả năng gửi/bài làm việc với chủ đề chính) .Vấn đề ở đây là ứng dụng UI gốc: Nó có khả năng (nghĩa là nó có một máy bơm tin nhắn), nhưng bối cảnh Đồng bộ hóa hiện tại là null và do đó tôi không thể phân biệt nó với trường hợp ứng dụng Console. Nếu tôi có thể phân biệt, thì tôi có thể đơn giản cài đặt một WindowsFormsSynchronizationContext trên chủ đề chính, và tôi tốt để đi.
  2. A Dispatcher: Chụp hình này bằng cách sử dụng Current tạo SynchronizationContext mới. Vì vậy, trong mọi tình huống tôi sẽ lấy lại một Dispatcher. Tuy nhiên, đối với ứng dụng Console, sử dụng Dispatcher.Invoke từ chuỗi nền sẽ treo (như mong đợi). Tôi có thể sử dụng Dispatcher.FromThread (mà không tạo ra một Dispatcher cho chủ đề nếu một không tồn tại). Nhưng ứng dụng UI gốc sẽ trả về một Dispatcher rỗng bằng cách sử dụng phương thức này, và vì vậy, tôi lại bị kẹt không thể phân biệt ứng dụng giao diện người dùng với ứng dụng giao diện điều khiển.
  3. A TaskScheduler: Tôi có thể sử dụng FromCurrentSynchronizationContext. Điều này có cùng một vấn đề như SynchronizationContext. I E. Trước khi gọi FromCurrentSyncronizationContext, tôi phải kiểm tra xem Current SynchronizationContext có là null hay không (đây là trường hợp của ứng dụng Console và ứng dụng ui gốc). Vì vậy, một lần nữa tôi không thể phân biệt ứng dụng gốc ui từ ứng dụng giao diện điều khiển.

Tôi, tất nhiên, có thể cho người dùng thư viện của tôi chỉ định ứng dụng UI khi họ gọi phương thức Initialize của tôi, nhưng tôi hy vọng tránh sự phức tạp đó đối với người dùng thư viện nếu có thể .

+0

Tôi đang sử dụng thư viện C# trong MFC. Thư viện này đã nhận được cuộc gọi TaskScheduler.FromCurrentSynchronizationContext(). khi tôi chạy ứng dụng MFC nó ném ngoại lệ nói rằng 'hiện tại SynchronizationContext có thể không được sử dụng như một TaskScheduler'.Any ý tưởng làm thế nào để đối phó với nó?Tôi không trực tiếp sử dụng thư viện C# trong MFC, nhưng tôi đã tạo ra một wrapper trong quản lý C + + và tôi đang sử dụng wrapper này trong ứng dụng MFC. –

Trả lời

5

Điều này không thể nói chung, một thư viện được sử dụng trong chủ đề không thể đưa ra bất kỳ giả định nào về chuỗi cụ thể nào là chuỗi giao diện người dùng. Bạn có thể capture Synchronization.Current nhưng nó sẽ chỉ hoạt động chính xác nếu phương thức khởi tạo của bạn được gọi từ thread UI. Đó không phải là quá bất thường để làm việc tốt, như TaskScheduler.FromCurrentSynchronizationContext() có xu hướng làm việc một cách tình cờ, nhưng không phải là sự bảo đảm. Bạn có thể thêm một kiểm tra, nếu Thread.CurrentThread.GetApartmentState() không trả về STA thì tỷ lệ cược mà bạn không được gọi từ thread UI là rất cao. SynchronizationContext.Current cũng sẽ thường là null trong trường hợp đó, một cách khác để kiểm tra.

Cách tốt nhất (cho là) ​​là đừng lo lắng về nó và để mã khách hàng tìm ra, nó sẽ không gặp bất kỳ sự cố nào khi sắp xếp lại cuộc gọi lại. Hoặc để trưng ra một thuộc tính của kiểu SynchronizationContext để mã máy khách có thể gán nó. Hoặc thêm nó làm đối số hàm tạo. Ném một InvalidOperationException nếu bạn đã sẵn sàng để đăng bài nhưng tìm ra rằng nó vẫn còn null, đó là một giám sát mà các lập trình viên khách hàng chỉ làm cho một lần.

1

Tôi nghĩ rằng bạn nên đặt tùy chọn này ở phương thức Initialize của bạn (hoặc bằng cách nào đó cho phép người gọi yêu cầu tương tác giao diện người dùng) với tôi. Tôi không biết chi tiết cụ thể nhưng dường như tôi là một điều "lịch sự" để làm, hãy để người gọi của bạn quyết định xem họ có muốn bạn hoặc muốn hỗ trợ giao diện người dùng của bạn hay không. Tôi sẽ tiến thêm một bước nữa và ngay cả khi người gọi cung cấp ngữ cảnh đồng bộ hóa. Nhưng đó là ý kiến ​​của tôi.

Để trả lời câu hỏi của bạn, có một vài "hack" bạn có thể sử dụng để xác định xem bạn có đang chạy trong bảng điều khiển ứng dụng hay không.Câu hỏi SO này có một số thông tin về điều đó: C#/.NET: Detect whether program is being run as a service or a console application

+0

Cảm ơn. Đối với một ứng dụng bản địa MFC gì SynchronizationContext họ nên cung cấp? Một WindowsFormsSynchronizationContext sẽ làm việc tốt, nhưng nó có vẻ hơi lạ đối với họ để cung cấp điều đó. –

+1

Tôi sẽ phải làm một chút nghiên cứu hoặc tham khảo ý kiến ​​một người quen thuộc hơn với MFC, nó đã được một _really_ thời gian dài kể từ khi tôi đã làm việc với MFC vì vậy tôi thậm chí không chắc chắn. Quan điểm của tôi chỉ là tôi không phải là một fan hâm mộ lớn của "nướng trong" những loại sự lựa chọn, sự cởi mở và linh hoạt thường trở thành một sự lựa chọn tốt hơn. – CodingGorilla

0

Thay đổi khởi tạo thư viện để có tham số SyncronizationContext. Nếu tham số là null thì thư viện không cần phải làm gì đặc biệt, nếu không null Post/Send GUI cập nhật ở đó.

-1

Tôi nghĩ rằng đây chính xác là những gì AsyncOperationManager.CreateOperation() dành cho. “Implementing the Event-based Asynchronous Pattern” tiểu bang:

Mẫu không đồng bộ dựa trên sự kiện cung cấp cách tiêu chuẩn để đóng gói một lớp có tính năng không đồng bộ. Nếu được triển khai với các lớp trợ giúp như AsyncOperationManager, lớp của bạn sẽ hoạt động chính xác theo bất kỳ mô hình ứng dụng nào, bao gồm các ứng dụng ASP.NET, Console và các ứng dụng Windows Forms.

Người gọi có quyền quyết định xem họ có muốn gọi API của bạn trên chuỗi giao diện người dùng hay không. Nếu họ làm, điều này sẽ nắm bắt bối cảnh và các sự kiện sẽ đi qua bơm thông điệp theo thứ tự. Trong ứng dụng Console, bạn có thể nhận được cùng một hành vi nếu bạn cài đặt SynchronizationContext như bạn nhận được miễn phí bằng cách sử dụng AsyncContext.Run() từ gói gói Nito.AsyncEx. Không cần thêm tài sản hoặc phải tự viết mã điều kiện. Nếu không có bối cảnh đồng bộ hóa tuần tự hóa có sẵn, AsyncOperation.Post() sẽ sử dụng ngữ cảnh đồng bộ hóa giả có sẵn cho các ứng dụng Console chỉ xếp hàng sự kiện vào luồng thay thế (nghĩa là các bài đăng có thể không thực hiện theo thứ tự). Chỉ cần nhớ gọi số AsyncOperation.OperationCompleted() hoặc AsyncOperation.PostOperationCompleted() khi hoàn tất.

Trong thư viện, tôi muốn chụp một cái gì đó (A SynchronizationContext, một dispatcher, một Task Scheduler, hay cái gì khác) trong quá trình khởi

Đây chính là điều AsyncOperationManager.CreateOperation() không, và trong một môi trường - cách độc đoán. Nhưng bạn nên cố gắng ghép nối điều này với một cuộc gọi đến OperationCompleted() mà có thể sẽ khó khăn hơn cho API mà bạn muốn hiển thị. Cách dễ nhất để sử dụng AsyncOperation sẽ là bắt đầu một hoạt động khi thư viện của bạn thực sự bắt đầu một hoạt động thay vì trong quá trình khởi tạo. Hoặc bằng cách khởi tạo thường trình trả về một đối tượng bối cảnh IDisposable đối tượng sẽ báo hiệu cho người tiêu dùng mà họ cần để quản lý tuổi thọ của nó.

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