2013-10-26 14 views
0

我正在編寫一個使用.NET中的提供者模型模式的小型應用程序。每個提供者都有一個方法可以實現,以便在我的應用程序中插入。C#異步,完成一個任務時無需訪問代碼即可停止所有任務

我想在不同的線程中運行所有提供者的具體工作(實現的方法),但是當一個任務完成時,所有其他任務需要停止執行。

我使用.NET中的異步/等待模型和CancellationToken來取消任務。

tasks.Add(Task.Run(() => 
       { 
         if (token.IsCancellationRequested) 
          token.ThrowIfCancellationRequested(); 

         return provider.DoWork(); 

       }, token)); 

while (tasks.Count > 0) 
     { 
      var t = await Task.WhenAny(tasks); 

      tasks.Remove(t); 

      var result = await t; 

      if (!string.IsNullOrEmpty(result)) 
      { 
       tokenSource.Cancel(); 
       return result; 
      } 
     } 

一些provider.DoWork()方法可能會非常緩慢,並停止在DoWork的方法執行我要的DoWork方法內檢查token.IsCancellationRequested線程,但我不能因爲這些方法都寫例如,來自其他人。 DoWork之外的token.IsCancellationRequested檢查在這種情況下並不重要。總之,當一個任務完成時,其他任務必須停止,但不能訪問每個線程中運行的代碼。

期待聽到你的想法。

+1

「最快速的供應商獲勝」是一個奇怪的模型,我不得不說 –

+1

如果你不能相信供應商合作,並定期檢查取消令牌,那麼我懷疑你將不得不寫代碼管理提供程序的線程,並中止/中斷您希望停止的那些提供程序的線程。 –

+0

@MarcGravell想象一下,提供者是一些用於搜索某些內容的html解析器,當其中一個人發現它並且其他人正在等待來自站點的響應5-10秒時,爲什麼我應該等待10秒。實際上,作爲代碼,我(作爲客戶端)將看到搜索結果,但任務不會被終止,並且CPU 100%是不必要的。 –

回答

0

有幾種不同的可能的方法來阻止任意代碼,但都不是理想的。

首先,您可以在線程級別上訪問它。換句話說,給每個提供者一個獨立的線程,並在你想要停止時調用Thread.Abort。不幸的是,這種方法存在許多問題:其中兩個更爲人所知的問題是死鎖的可能性,並進入奇怪的狀態,在這種狀態下,AppDomain中不能再實例化類型。由於這個原因,Thread.Abort被認爲是「邪惡」和一個確定的代碼味道。

移動一個更高的級別,可以在AppDomain級別上訪問此級別。在這種情況下,您爲每個提供商分配一個AppDomain。這種方法的優點是你可以比線程更乾淨地關閉AppDomain。不幸的是,有些東西仍然可以通過裂縫:特別是,非託管資源可能會從AppDomain泄漏到您的進程中。

這爲您留下了最可靠的解決方案:在流程級別上接近這一點。也就是說,您給每個提供商一個單獨的流程並使用進程間通信來控制它。由於操作系統負責清理,因此可以徹底關閉進程。不幸的是,這種方法的開銷最大,而且最難編程。

強大的託管解決方案必須至少支持最後一個。例如,ASP.NET支持以下三種情況:在某些情況下請求線程可以中止(例如,客戶端斷開連接),AppDomain可以被回收,並且這可以升級到整個工作進程被回收的地方。但是,我建議您不要使用升級策略編寫自己的主機平臺。

我推薦的是1)將CancellationToken提供給您的提供者,並鼓勵他們使用它; 2)在發出令牌信號後,讓它們運行完成。

+0

感謝您的詳細回覆。關於將CancellationToken傳遞給提供者的主要問題是:1)通過這種方式在提供者實現的方式和他們執行的上下文(異步,同步等)之間創建了某種連接。 2)提供者必須知道什麼是CancellationToken以及如何使用它。 3)不能正確處理令牌的提供商可能會減慢整個系統(解決方案:正如@Marc所說的 - 忽略慢速提供商或設置某種超時)。另一方面,當您使用外部庫或API時,您應該瞭解如何使用它,對。 –

+0

我不確定你的意思是(1); 'CancellationToken'是同步和異步代碼的標準取消機制。我認爲(2)不是一個難題。就(3)而言,管理愚蠢代碼的*唯一可靠的方法是在一個單獨的過程中。 –

2

在進程中突然終止一個線程有很多問題 - 實質上,只有在進程非常噁心以至於要殺死整個進程時才應該這樣做。除此之外的任何事情,如果您干涉,您將面臨嚴重問題。所以:那會讓優雅的選擇退出。你提到你已經在使用取消令牌;真的,這就是你所能做的(至少安全)。由不同的實現來檢查取消和退出。如果你不能依靠定期檢查取消的任務:你有點卡住了。

基本上,對於您所描述的內容來說,沒有簡單的答案。