2012-06-11 19 views
7

我有一個C#庫,希望能夠發送/張貼工作到「主」UI線程(如果存在)。 這個庫可以通過使用:從庫中捕獲主線程SynchronizationContext或Dispatcher

  • WinForms應用程序
  • 本地應用程序(與UI)
  • 控制檯應用程序(沒有UI)

在圖書館我想喜歡在初始化過程中捕獲某些內容(A SynchronizationContext,Dispatcher,Task Scheduler或其他),這將允許我(稍後)發送/發佈工作到主線程(如果主線程具有該能力 - - 它有一個消息泵)。例如,當且僅當主應用程序有能力讓我進入主線程時,庫纔會在主線程上放置一些Winforms UI。

事情我已經嘗試:

  1. 一個SynchronizationContext: 捕捉這工作正常的WinForms應用程序(一WindowsFormsSynchronizationContext將被安裝爲Current的SynchronizationContext這也工作正常的控制檯應用程序 - 因爲我可以檢測到Current SynchronizationContext爲空(因此,我知道我沒有能力向主線程發送/發佈工作)。這裏的問題是本地UI應用程序:它有能力(即它有一個消息泵),但當前同步上下文是空的,因此我無法區分它與控制檯應用程序的情況。如果我能區分,那麼我可以簡單地在主線程上安裝WindowsFormsSynchronizationContext,我很高興。
  2. A Dispatcher:使用Current捕獲該文件會創建一個新的SynchronizationContext。因此,在所有情況下,我都會找回一個調度員。但是,對於控制檯應用程序,從後臺線程使用Dispatcher.Invoke將會掛起(如預期的那樣)。我可以使用Dispatcher.FromThread(如果線程不存在,則不會爲線程創建分派器)。但是,本地UI應用程序將使用此方法返回一個空分派器,因此我再次陷入不能將UI應用程序與控制檯應用程序區分開來。可以使用FromCurrentSynchronizationContext。這與SynchronizationContext有相同的問題。即在調用FromCurrentSyncronizationContext之前,我必須檢查Current SynchronizationContext是否爲null(控制檯應用程序和本地ui應用程序將會是這種情況)。因此,我再也無法從控制檯應用程序中區分本地ui應用程序。

我,當然,也有我的媒體庫的用戶指定它是否是當他們打電話給我Initialize方法的UI應用程序,但我希望以避免併發症的可能的話文庫的用戶。

+0

我在MFC中使用C#庫。這個庫調用了TaskScheduler.FromCurrentSynchronizationContext()。當我運行MFC應用程序時,它會拋出異常,說'當前的SynchronizationContext不能用作TaskScheduler'。任何想法如何處理它?我沒有直接在MFC中使用C#庫,但我已經在託管C++中創建了一個包裝器,並且我在MFC應用程序中使用了這個包裝器。 –

回答

5

這不是一般可能的,易於在線程中使用的庫不能對任何特定線程是UI線程做出任何假設。您可以捕獲Synchronization.Current,但只有在從UI線程調用初始化方法時才能正常工作。這對於解決問題並不是非常不尋常的,就像TaskScheduler.FromCurrentSynchronizationContext()往往意外地工作,但不是保證。你可以添加一個檢查,如果Thread.CurrentThread.GetApartmentState()沒有返回STA,那麼你沒有從UI線程調用的可能性非常高。在這種情況下,SynchronizationContext.Current通常也會是null,這是另一種檢查方式。

(可以說)更好的方法是不用擔心它,讓客戶端代碼找出它,它不會有任何麻煩的封送回調。或者公開一個SynchronizationContext類型的屬性,以便客戶端代碼可以分配它。或者將其添加爲構造函數參數。如果您準備發佈但發現它仍然爲空,則拋出InvalidOperationException,這是客戶機程序員只做過一次的疏忽。

1

我想你應該使這個選項對你的Initialize方法(或以某種方式允許你的調用者請求UI交互),對我來說只是更有意義。我不知道具體細節,但在我看來這是一件「有禮貌」的事情,讓你的來電者決定他們是否想要你或想支持你的用戶界面。我會更進一步,甚至作爲調用者提供同步上下文。但這是我的看法。

要回答你的問題,有幾個「黑客」可以用來確定你是否在控制檯應用程序中運行。這個SO問題有一些信息:C#/.NET: Detect whether program is being run as a service or a console application

+0

謝謝。對於本機MFC應用程序,應該提供什麼SynchronizationContext? WindowsFormsSynchronizationContext可以正常工作,但它們似乎有點奇怪。 –

+1

我將不得不做一些研究或諮詢熟悉MFC的人,因爲我已經使用MFC很長一段時間了,所以我甚至不確定。我的意思是,我不是那種「烘烤」這種選擇的大粉絲,開放性和靈活性通常是一個更好的選擇。 – CodingGorilla

0

將庫初始化更改爲具有SyncronizationContext參數。如果參數爲null,那麼庫不需要做任何特殊的事情,如果不是空Post/Send GUI更新那裏。

-1

我認爲這正是AsyncOperationManager.CreateOperation()的目的。 「Implementing the Event-based Asynchronous Pattern」 states:

基於事件的異步模式提供了一種標準化的方法來打包具有異步功能的類。如果使用類似AsyncOperationManager等助手類實現,您的類將在任何應用程序模型(包括ASP.NET,控制檯應用程序和Windows窗體應用程序)下正常工作。

由調用者決定是否要在UI線程上調用您的API。如果他們這樣做,這將捕獲上下文,並且事件將按順序通過消息泵。在控制檯應用程序中,如果您安裝了SynchronizationContext,您可以通過使用Nito.AsyncEx nuget包中的AsyncContext.Run()免費獲得同樣的行爲。不需要額外的屬性或必須自己編寫條件代碼。如果沒有序列化同步上下文可用,AsyncOperation.Post()將使用可用於控制檯應用程序的僞同步上下文,該應用程序僅將事件排列到線程池(意思是帖子可能不按順序執行)。請記住,完成後請致電AsyncOperation.OperationCompleted()AsyncOperation.PostOperationCompleted()

在圖書館我想拍攝的東西(A的SynchronizationContext,一調度,任務計劃程序,或者別的什麼)在初始化過程中

這正是AsyncOperationManager.CreateOperation()做,並在環境不可知論的方式。但是,您應該嘗試將此與調用OperationCompleted()配對,由於您想要公開API,這可能會更困難。使用AsyncOperation最簡單的方法是在庫啓動操作而不是初始化時啓動操作。或者通過初始化例程返回一個IDisposable上下文對象句柄,這個句柄將向消費者發出他們需要管理其生存期的信號。