2014-11-08 117 views
2

我見過關於如何殺死一個鎖定線程的許多問題,並且我見過的每個答案都說不殺線程,甚至不使用Thread.Abort,只需使用良好的編程習慣來處理線程應該停止的時間。如何在C中處理/殺死一個鎖定線程#

我的問題是,我卡住了一箇舊的第三方庫,我無法控制,而且有一次,這個鎖定,使用幾乎100%的CPU,從不放手。 Thread.Abort不會阻止這一點。我找不到任何方法來阻止此事。

我發現最好的解決方法是使用委託和.AsyncWaitHandle.WaitOne(10000,false);

代碼將在超時後繼續從這一點開始,但是仍然運行的線程繼續運行並耗盡我所有的CPU。

我該如何處理這種情況?如果這個第三方庫鎖定,我需要一種方法來殺死它。

感謝您的時間, 本

+0

你如何從這個庫調用代碼?你是否明確地創建了一個新的線程,如果是的話,怎麼樣?一段代碼將會很有用。 – 2014-11-08 14:30:35

+1

這是不可能的,只是殺死線程。唯一可以安全殺死的就是整個過程。 '線程。Abort'只會在線程執行託管代碼時工作,所以對於本地的第三方庫來說,這無濟於事。因此,將違規代碼分離成一個新的簡單流程,您可以從您的應用程序運行並在需要時終止。 – Luaan 2014-11-08 15:09:18

回答

5

如果你需要一個籠統的回答,請與該會鎖住代碼一個單獨的進程。根據需要殺死並重新啓動進程。隨着對所面臨的確切問題的更多瞭解,可能會有更好的解決方案。

1

我會創建一個單獨的應用程序域來運行一個不受信任的程序集,或者就像你的情況那樣不時工作。應用程序域可以隨時卸載,它將在您的代碼和外部代碼之間創建一個邊界。這是一個小例子。讓我們先從包含不正確的代碼外部組件:

public class Processor 
{ 
    public void Run() 
    { 
     while (true) { /* ... */ }     
    } 
} 

現在,我們需要一個類,將加載BadAssembly.dll,創建處理器的一個實例,並調用運行方法在單獨的線程中爲了不阻止你的應用程序的主線程。

public class Loader : MarshalByRefObject 
{ 
    public bool IsCompleted { get; private set; } 
    public bool IsFaulted { get; private set; } 

    public void LoadAndRunBadAssembly() 
    { 
     var ass = Assembly.LoadFrom("BadAssembly.dll"); 
     var c = (Processor)ass.CreateInstance("BadAssembly.Processor"); 

     Task.Factory.StartNew(() => 
      { 
       try 
       { 
        c.Run(); 
        IsCompleted = true; 
       } 
       catch (Exception) 
       { 
        IsFaulted = true; 
       } 
      }, TaskCreationOptions.LongRunning); 
    } 
} 

IsCompletedIsFaulted性需要,讓我們知道,如果外部的代碼已經處理完畢或沒有。 加載程序MarshalByRefObject派生,因爲它的代碼將在我們創建的獨立域中執行。這裏是一個代碼來測試裝載機類:

var appDomain = AppDomain.CreateDomain("Loader"); 
try 
{ 
    //Here I assume that Loader class is in the same assembly as this code 
    var loader = (Loader)appDomain.CreateInstanceAndUnwrap(
     Assembly.GetExecutingAssembly().FullName, 
     typeof (Loader).FullName); 

    loader.LoadAndRunBadAssembly(); 

    Console.WriteLine("Waiting..."); 

    Thread.Sleep(20000); //This simulates waiting for external code 

    if (loader.IsCompleted) 
     Console.WriteLine("Processing has finished"); 
    else if (loader.IsFaulted) 
     Console.WriteLine("There was an error"); 
    else 
     Console.WriteLine("It lasts too long"); 
} 
catch (Exception ex) 
{ 
    Console.WriteLine(ex); 
} 
finally 
{ 
    AppDomain.Unload(appDomain); 
} 

UPDATE

我剛剛意識到,爲了停止線程在一個域中AppDomain.Unload方法使用中止方法。這意味着您在嘗試卸載域時可能會收到CannotUnloadAppDomainException異常。它會發生,因爲不能保證中止會立即殺死一個線程。

+1

如果appdomain中的線程無法中止,則該域不會卸載。 – usr 2014-11-08 16:29:20

+0

我運行此代碼幾次,它的工作。例如,嘗試將Console.Writeline放入Processor.Run方法的while循環中並運行該程序。 20秒後,控制檯中不會顯示新消息。您也可以在Debug中運行我的代碼,並觀察Visual Studio中的「模塊」窗口。 20年後BadAssembly.dll將從列表中刪除,這意味着應用程序域被卸載。類似的練習可以通過線程窗口進行。我錯過了什麼? – 2014-11-08 17:56:07

+0

是的,說'Thread.Abort()'的問題的部分不能阻止這一點,這表明掛起處於非託管代碼中。 – 2014-11-08 18:22:47