启动多个线程并从我的.NET应用程序中跟踪它们

我想从我的.NET应用程序启动x个线程,我想跟踪它们,因为我需要手动终止它们,或者我的应用程序稍后关闭我的应用程序。

示例==>启动线程Alpha,启动线程测试..然后在我的应用程序中的任何点我应该能够说终止线程测试..

在.NET中跟踪打开的线程的最佳方法是什么?我需要知道什么(一个id?)关于一个线程来终止它? 示例代码,教程会很有帮助。

你可以节省自己的驴工作并使用这个智能线程池 。 它提供了一个工作单元系统,允许您在任何时候查询每个线程的状态,并终止它们。

如果这太麻烦了,那么正如提到的IDictionary可能是最简单的解决方案。 或者更简单的是给每个线程一个名称,并使用IList

 public class MyThreadPool { private IList _threads; private readonly int MAX_THREADS = 25; public MyThreadPool() { _threads = new List(); } public void LaunchThreads() { for (int i = 0; i < MAX_THREADS;i++) { Thread thread = new Thread(ThreadEntry); thread.IsBackground = true; thread.Name = string.Format("MyThread{0}",i); _threads.Add(thread); thread.Start(); } } public void KillThread(int index) { string id = string.Format("MyThread{0}",index); foreach (Thread thread in _threads) { if (thread.Name == id) thread.Abort(); } } void ThreadEntry() { } } 

当然,您可以更多地参与其中并使其复杂化。 如果杀死你的线程不是时间敏感的(例如,如果你不需要在UI中3秒内杀死一个线程),那么Thread.Join()是一种更好的做法。

如果您还没有阅读过,那么Jon Skeet就SO上常见的“不要使用中止”建议进行了很好的讨论和解决方案 。

您可以创建一个线程字典并为它们分配id,如:

 Dictionary threads = new Dictionary(); for(int i = 0 ;i < numOfThreads;i++) { Thread thread = new Thread(new ThreadStart(MethodToExe)); thread.Name = threadName; //Any name you want to assign thread.Start(); //If you wish to start them straight away and call MethodToExe threads.Add(id, thread); } 

如果您不想针对Id保存线程,则可以使用列表,稍后只需枚举它以终止线程。

当你希望终止它们时,你可以中止它们。 最好在MethodToExe中有一些条件允许该方法允许线程正常终止。 就像是:

 void MethodToExe() { while(_isRunning) { //you code here// if(!_isRunning) { break; } //you code here// } } 

要中止,您可以枚举字典并调用Thread.Abort() 。 准备好捕获ThreadAbortException

我问过类似的问题并得到了一些很好的答案: 关闭multithreading应用程序

注意:我的问题不需要优雅的退出,但人们仍然建议我优雅地退出每个线程的循环。

要记住的主要事情是,如果您想避免让线程阻止您的进程终止,您应该将所有线程设置为后台:

 Thread thread = new Thread(new ThreadStart(testObject.RunLoop)); thread.IsBackground = true; thread.start(); 

启动和管理线程的首选方法是在ThreadPool ,但几乎任何容器都可用于保持对线程的引用。 你的线程应该总是有一个标志,告诉他们终止,他们应该不断检查它。

此外,为了更好地控制,您可以为您的线程提供CountdownLatch :每当线程退出其循环时,它将在CountdownLatch上发出信号。 您的主线程将调用CountdownLatch.Wait()方法,它将阻塞,直到所有线程都已发出信号…这允许您正确清理并确保在开始清理之前所有线程都已关闭。

 public class CountdownLatch { private int m_remain; private EventWaitHandle m_event; public CountdownLatch(int count) { Reset(count); } public void Reset(int count) { if (count < 0) throw new ArgumentOutOfRangeException(); m_remain = count; m_event = new ManualResetEvent(false); if (m_remain == 0) { m_event.Set(); } } public void Signal() { // The last thread to signal also sets the event. if (Interlocked.Decrement(ref m_remain) == 0) m_event.Set(); } public void Wait() { m_event.WaitOne(); } } 

值得一提的是Thread.Abort()方法做了一些奇怪的事情:

当一个线程自身调用Abort时,效果类似于抛出exception; ThreadAbortException立即发生,结果是可预测的。 但是,如果一个线程在另一个线程上调用Abort,则中止将中断正在运行的任何代码。 静态构造函数也有可能被中止。 在极少数情况下,这可能会阻止在该应用程序域中创建该类的实例。 在.NET Framework 1.0和1.1版本中,当finally块正在运行时,线程有可能中止,在这种情况下,finally块将被中止。

如果正在中止的线程位于受保护的代码区域(例如catch块,finally块或约束执行区域),则调用Abort的线程可能会阻塞。 如果调用Abort的线程持有中止线程所需的锁,则可能发生死锁。

创建线程后,您可以设置它的Name属性。 假设您将它存储在某个集合中,您可以通过LINQ方便地访问它以便检索(并中止)它:

 var myThread = (select thread from threads where thread.Name equals "myThread").FirstOrDefault(); if(myThread != null) myThread.Abort(); 

哇,答案太多了..

  1. 你可以简单地使用一个数组来保存线程,这只有在对数组的访问是sequantial时才有效,但是如果你有另一个线程访问这个数组,你将需要同步访问
  2. 您可以使用线程池,但线程池非常有限,只能容纳固定数量的线程。
  3. 如上所述,您可以创建自己的线程池,通过引入安全集合,.NET v4中的线程池变得更加容易。
  4. 您可以通过保存一个互斥对象列表来管理它们,该对象将确定这些线程何时完成,线程将在每次运行之前查询互斥锁,然后执行其他操作,如果它设置了,则终止,您可以从任何地方管理静音,并且由于互斥是通过防御线程安全的,它相当容易..

我可以想到另外10种方法,但这些似乎有效。 如果他们不符合您的需求,请告诉我。

取决于你需要多么复杂。 您可以使用辅助方法等实现自己类型的ThreadPool。但是,我认为它就像维护列表/数组并相应地向集合中添加/删除线程一样简单。

您还可以使用Dictionary集合并使用您自己的特定键类型来检索它们,即Guids / strings。

当您启动每个线程时,将其ManagedThreadId作为键放入Dictionary中,将线程实例作为值放入。 使用每个线程的回调来返回其ManagedThreadId,您可以使用它来终止时从Dictionary中删除该线程。 如果需要,您还可以使用Dictionary来中止线程。 使线程成为后台线程,以便在应用程序意外终止时终止它们。

您可以使用单独的回调来指示线程继续或暂停,这反映了UI设置的标志,以便正常退出。 您还应该在线程中捕获ThreadAbortException,以便在必须中止线程时可以进行任何清理。