[英]How do I communicate between multiple threads?
我正在為另一個程序編寫一個插件,該程序使用本機程序打開一系列文件以從中提取一些數據。 我遇到的一個問題是該過程需要很長時間,我想保持用戶界面不掛。 另外,我還想讓用戶在完成之前取消該過程。 在過去,我使用后台工作者來處理這類事情,但在這種情況下,我認為BackgroundWorker不會起作用。
要通過API創建插件,我使用的可以通過繼承IAPICommand接口來創建自定義命令。 該接口包括Execute(應用程序app)方法。 然后實例化該類,當用戶在程序中喚起自定義命令時,程序將調用Execute()方法。
Execute()方法在調用時傳遞對當前Application對象的引用,並且該應用程序對象用於打開文件以從中提取數據。 但是,應用程序實例無法在原始Execute()線程之外的線程請求時打開文檔。
因此,通常UI將存在於主線程上,並且將在輔助線程上執行耗時的數據提取。 但是,在這種情況下,必須在主線程上執行數據提取,並且我需要為UI創建輔助線程。
這是代碼的精簡版本。
class MyCommand:IAPICommand
{
public void Execute(Application app) // method from IAPICommand
{
Thread threadTwo= new Thread(ShowFormMethod);
threadTwo.Start();
}
public void ProcessWidget(Widget w, Application app)
{
//uses an App to work some magic on C
//app must be called from the original thread that called ExecuteCommand()
}
//method to open custom form on a seperatethread
public void ShowFormMethod()
{
MyForm form = new MyForm();
form.ShowDialog();
}
}
這是一個流程圖,顯示了我認為這應該最終起作用的方式。
alt text http://dl.dropbox.com/u/113068/SOMLibThreadingDiagram.jpg
在應用程序中創建多個創建表單的線程通常是個壞主意。 使這項工作並非不可能,但它比你想象的要困難得多,因為父子關系中的表單會相互發送消息,而當它們發送消息時,發送消息的表單會阻塞接收處理它。
將此信息與您明確執行的線程之間的消息傳遞或同步相結合,並且很容易導致死鎖。 因此,一般情況下,最好確保為用戶界面保留主線程,並在沒有UI的其他線程中執行所有處理。
如果符合該設計,則后台線程可以使用Control.BeginInvoke將消息傳遞給UI線程,而無需等待消息處理。
除了其他答案,我建議您使用ProcessWidget中的回調方法將進度傳遞回調用線程。 要過早地停止工作線程,如果它經常更新調用者,您可以使用回調將停止信號返回到工作線程。 或者使用單獨的回調方法定期檢查go / no-go。 或者設置一個(gasp!)全局靜態標志,工作者定期檢查。 或者在工作線程上調用Thread.Abort並讓它捕獲ThreadAbortException以清理任何資源。
我假設主機應用程序是一個WinForms應用程序。
您需要從Execute
方法中的原始線程保存SynchronizationContext ,然后調用其Send
方法在主機的UI線程上執行代碼。
例如:
class MyCommand:IAPICommand
{
SynchronzationContext hostContext;
public void Execute(Application app) // method from IAPICommand
{
hostContext = SynchronzationContext.Current;
Thread threadTwo = new Thread(ShowFormMethod);
threadTwo.Start();
}
public void ProcessWidget(Widget w, Application app)
{
//uses an App to work some magic on C
//app must be called from the original thread that called ExecuteCommand()
SomeType someData = null;
hostContext.Send(delegate { someData = app.SomeMethod(); }, null);
}
}
如果你看一下Java swing,它就是一個很好的例子:
1)主線程負責處理所有UI請求。 這將從應用程序中刪除任何競爭條件。
2)任何時候要完成任何“工作”,產生一個線程(或一個線程池)並完成工作。 因此,除了幾微秒之外,主線程不會被阻止,並且無論發生什么,UI都是完全響應的。
3)在所有語言中都必須有線程中斷機制。 在java中,您在線程上調用.interrupt(),並且當前運行的線程在執行任何地方時都會拋出InterruptedException。 你的工作是抓住那個異常,弄清楚你是否真的被打斷了(讀這個部分的javadocs),如果你只是讓自己死(退出run方法)。
1 + 2 =不引人注目的客戶互動
3 =殺死線程
3的替代(如果3太復雜)是給線程一個方法.kill(); 該方法設置一個kill標志。 當您從循環中的硬盤驅動器中讀取緩沖區時,請檢查是否設置了kill標志,是否已經脫離循環,關閉處理程序,並返回run方法。
編輯:抱歉忘了提及進度報告:
您的線程應該具有公開的線程安全方法來獲取“進度報告”,或者更確切地說是包含有關進度信息的數據結構。 您的UI線程應定期(比如說每0.5秒)檢查線程的進度報告並更新UI的進度條。 通過UI線程檢查,我的意思是顯示進度的窗口小部件請求使用計時器的最新信息重新呈現,直到完成。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.