2014-11-08 177 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