简体   繁体   English

在多线程C#应用程序中正确终止线程

[英]Proper thread termination in multi-threaded C# application

I have what I think is a rather complex problem. 我认为这是一个相当复杂的问题。 I have a C# application that utilizes a plug-in architecture using reflection. 我有一个C#应用程序,它使用反射的插件架构。 The application loads plug-in dlls and the user is able to enable/disable them. 应用程序加载插件dll,用户可以启用/禁用它们。 When a plug-in is enabled, the main app starts a thread to run the plug-in in. In many cases the plug-in may have multiple threads of its own. 启用插件后,主应用程序会启动一个线程来运行插件。在许多情况下,插件可能有多个自己的线程。 When I want to disable a plug-in I am calling Thread.Abort(). 当我想禁用一个插件时,我正在调用Thread.Abort()。 This seems to kill the initial thread that was created for the plug-in, but any additional threads that the plug-in created continue running. 这似乎会破坏为插件创建的初始线程,但插件创建的任何其他线程都会继续运行。 Is there a better way to stop all of the associated plug-in's threads along with the main thread? 有没有更好的方法来阻止所有相关的插件线程和主线程?

Thanks. 谢谢。

Don't use Thread.Abort for this purpose. 不要将Thread.Abort用于此目的。

Add a Shutdown method or similar to your plugin API and ask your plugins to shutdown cleanly. 添加Shutdown方法或类似于您的插件API,并要求您的插件干净地关闭。 If you need to protect yourself against rogue plugins that refuse to shutdown then run them in their own AppDomain and kill the AppDomain if necessary. 如果您需要保护自己免受拒绝关闭的恶意插件的影响,请在他们自己的AppDomain中运行它们并在必要时终止AppDomain。

I would set a flag in the plugin that I wish to close it, and the plugin itself should periodically check to see if the flag is set and if so, it should clean up after itself. 我会在插件中设置一个我希望关闭它的标志,插件本身应该定期检查标志是否已设置,如果是,它应该自行清理。 This aproach works best with a while(true) {...} kind of thread body. 这种方法最适合使用一段while(true) {...}种线程体。

If instead you're using an event-based aproach with a lot of WaitForSingleObject , I would use an event that I'd pulse when I want the plugin to shut down, and I'd add the event to a WaitForMultipleObjects list to be checked. 如果您使用基于事件的aproach与大量的WaitForSingleObject ,我会使用一个事件,当我想要插件关闭时,我将脉冲,我将事件添加到WaitForMultipleObjects列表进行检查。

It really depends on how your plugin is running and how exactly it's coded. 这实际上取决于您的插件如何运行以及它的编码方式。 Try providing more information. 尝试提供更多信息。

Thread.Abort is a horrible way to do it, it doesn't run any cleanup at all. Thread.Abort是一种可怕的方式,它根本不进行任何清理。

Yeah, don't use Abort. 是的,不要使用Abort。 I'd use a ManualResetEvent to signal when I want to terminate the execution, eg with a loop: 当我想终止执行时,我会使用ManualResetEvent来发出信号,例如使用循环:

ManualResetEvent _stopping = new ManualResetEvent(false);
...
while (!_stopping.WaitOne(0))
{
    ...
}

And signal when you want to stop the loop: 当你想要停止循环时发出信号:

public void Stop()
{
    _stopping.Set();
}

Then use Thread.Join, if you want to wait until the thread terminates. 然后使用Thread.Join,如果你想等到线程终止。 If it doesn't in a timely fashion you then could call Abort. 如果没有及时,你可以打电话给Abort。

In your case you should REALLY load the add-ins in a separate AppDomain and unload it when you are done. 在您的情况下,您应该真正将加载项加载到单独的AppDomain中,并在完成后卸载它。 You may want to architecture a model in which you also send to the add-ins notifications when they are about to be shutdown. 您可能希望构建一个模型,在该模型中,当它们即将关闭时,您也会发送加载项通知。

First, you really should avoid using Thread.Abort as a means to terminate a thread. 首先,你真的应该避免使用Thread.Abort作为终止线程的手段。 I don't know if you have control over the implementatio of the plug-in, but if you do you should reconsider that design. 我不知道你是否可以控制插件的实现,但如果你这样做,你应该重新考虑这个设计。 Read this and this for more details about the problems related to Thread.Abort . 阅读有关相关问题的更多细节Thread.Abort

As for how to have other threads stop that the plug-in creates, again - you want to design an interface between your code and the plug in that gracefully informs the plug in that you would like it to stop what it's doing. 至于如何让其他线程停止插件创建,再次 - 你想在你的代码和插件之间设计一个接口,优雅地通知插件你希望它停止它正在做的事情。

I know of no way to interrogate a Thread for clues about who may claim to be its owner. 我知道没有办法询问线程有关谁可能声称自己是拥有者的线索。 That basically means you will need to add the approriate mechanisms to your plugin API for shutting down an instance of plugin. 这基本上意味着你需要在你的插件API中添加approriate机制来关闭插件实例。 Maybe you could add a Shutdown or Terminate method to the plugin interface and make it part of documented contract that imlementers must play nice and cleanup all resources they allocate. 也许您可以在插件界面中添加ShutdownTerminate方法,并使其成为记录合同的一部分,这些合同必须能够很好地清理他们分配的所有资源。

The only other strategy I can think of (and this one sucks big time) is to keep track of all the threads your application creates on its own and then assume that all others are owned by plugins. 我能想到的唯一另一个策略(这个很糟糕的时候)是跟踪应用程序自己创建的所有线程,然后假设所有其他线程都由插件拥有。 You would have to abort all of those threads since you would not be able to link them to specific instances of a plugin. 您将不得不中止所有这些线程,因为您无法将它们链接到插件的特定实例。 Personally, this sounds like a recipe for disaster. 就个人而言,这听起来像是灾难的秘诀。 I would only explore this as a last resort. 我只会探索这个作为最后的手段。

It sounds to me like you're calling arbitrary methods on various assemblies that may not have been designed to be run on a thread with the expectation that they may be cancelled. 听起来像你在各种程序集上调用任意方法,这些方法可能不是设计为在线程上运行而期望它们可能被取消。
The plug-in method that you run on it's own thread should at least be handling exceptions and cleaning up it's own threads. 在它自己的线程上运行的插件方法至少应该处理异常并清理它自己的线程。 When Thread.Abort() is called on the thread, a ThreadAbortException is thrown on that thread to terminate it. 在线程上调用Thread.Abort()时,会在该线程上抛出ThreadAbortException以终止它。

Thread.Abort() is not the recommended way to terminate a thread under normal conditions. Thread.Abort()不是在正常条件下终止线程的推荐方法。 A better approach is to have a cooperative way designed into the method that allows you to cancel it. 更好的方法是在方法中设计一种合作方式,允许您取消它。 If that's not possible, calling Thread.Interrupt() is a gentler way to terminate the thread as long as the executing method occasionally enters a WaitSleepJoin state (waiting on an WaitHandle, sleeping, etc.) at safe points. 如果这是不可能的,只要执行方法偶尔在安全点进入WaitSleepJoin状态(等待WaitHandle,休眠等),调用Thread.Interrupt()是一种更温和的终止线程的方法。 In that case, a ThreadInterruptedException is thrown on the thread. 在这种情况下,线程上抛出ThreadInterruptedException。 The other threads should still be cleaned up in an exception handler. 其他线程仍应在异常处理程序中清理。

The other guys are right - code in a graceful shutdown signal. 其他人都是对的 - 在优雅的关机信号中编码。 However, if you can't readily change the child threads' code, and you're under awful schedule pressure, and if there's little chance that the child threads will reset or hang on an abort, and you promise to refactor this later, then you could add a fail-safe to your plugin that adds child threads to a collection, and then trap the ThreadAbortException and walk the collection aborting the child threads. 但是,如果您无法轻易更改子线程的代码,并且您的计划压力很大,并且如果子线程重置或挂起中止的可能性很小,并且您承诺稍后重构,那么您可以为插件添加一个故障保护,将子线程添加到集合中,然后捕获ThreadAbortException并使集合中止子线程。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM