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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2356130/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-07 01:42:59  来源:igfitidea点击:

Starting multiple threads and keeping track of them from my .NET application

c#multithreading

提问by G-Man

I would like to start x number of threads from my .NET application, and I would like to keep track of them as I will need to terminate them manually or when my application closes my application later on.

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

Example ==> Start Thread Alpha, Start Thread Beta .. then at any point in my application I should be able to say Terminate Thread Beta ..

示例 ==> Start Thread Alpha, Start Thread Beta .. 然后在我的应用程序中的任何一点我都应该能够说 Terminate Thread Beta ..

What is the best way to keep track of opened threads in .NET and what do I need to know ( an id ? ) about a thread to terminate it ? Sample code, tutorial would be helpful.

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

采纳答案by Chris S

You could save yourself the donkey work and use this Smart Thread Pool. It provides a unit of work system which allows you to query each thread's status at any point, and terminate them.

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

If that is too much bother, then as mentioned anIDictionary<string,Thread>is probably the simplest solution. Or even simpler is give each of your thread a name, and use an IList<Thread>:

如果这太麻烦了,那么如前所述 anIDictionary<string,Thread>可能是最简单的解决方案。或者更简单的是给你的每个线程一个名字,并使用一个IList<Thread>

public class MyThreadPool
{
    private IList<Thread> _threads;
    private readonly int MAX_THREADS = 25;

    public MyThreadPool()
    {
        _threads = new List<Thread>();
    }

    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()
    {

    }
}

You can of course get a lot more involved and complicated with it. If killing your threads isn't time sensitive (for example if you don't need to kill a thread in 3 seconds in a UI) then a Thread.Join() is a better practice.

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

And if you haven't already read it, then Jon Skeet has this good discussion and solutionfor the "don't use abort" advice that is common on SO.

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

回答by James

Depends on how sophisticated you need it to be. You could implement your own type of ThreadPool with helper methods etc. However, I think its as simple as just maintaining a list/array and adding/removing the threads to/from the collection accordingly.

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

You could also use a Dictionary collection and use your own type of particular key to retrieve them i.e. Guids/strings.

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

回答by Manu

After creating your thread, you can set it's Name property. Assuming you store it in some collection you can access it conveniently via LINQ in order to retrieve (and abort) it:

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

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

回答by MindFold

Wow, there are so many answers..

哇,答案太多了。。

  1. You can simply use an array to hold the threads, this will only work if the access to the array will be sequantial, but if you'll have another thread accessing this array, you will need to synchronize access
  2. You can use the thread pool, but the thread pool is very limited and can only hold fixed amount of threads.
  3. As mentioned above, you can create you own thread pool, which in .NET v4 becomes much easier with the introduction of safe collections.
  4. you can manage them by holding a list of mutex object which will determine when those threads should finish, the threads will query the mutex each time they run before doing anything else, and if its set, terminate, you can manage the mutes from anywhere, and since mutex are by defenition thread-safe, its fairly easy..
  1. 您可以简单地使用一个数组来保存线程,这仅在对数组的访问是连续的情况下才有效,但是如果您有另一个线程访问此数组,则需要同步访问
  2. 可以使用线程池,但线程池非常有限,只能容纳固定数量的线程。
  3. 如上所述,您可以创建自己的线程池,在 .NET v4 中,随着安全集合的引入,这变得更加容易。
  4. 您可以通过持有互斥对象列表来管理它们,该列表将确定这些线程应该何时完成,线程将在每次运行之前查询互斥,然后再执行任何其他操作,如果设置、终止,您可以从任何地方管理静音,并且由于互斥锁是防御线程安全的,因此相当容易..

i can think of another 10 ways, but those seems to work. let me know if they dont fit your needs.

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

回答by ata

You can create a Dictionary of threads and assign them id's, like:

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

Dictionary<string, Thread> threads = new Dictionary<string, Thread>();
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);
}

If you don't want to save threads against an Id you can use a list and later on just enumerate it to kill threads.

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

And when you wish to terminate them, you can abort them. Better have some condition in your MethodToExethat allows that method to leave allowing the thread to terminate gracefully. Something like:

当您希望终止它们时,您可以中止它们。最好在您的某些条件下MethodToExe允许该方法离开,从而允许线程正常终止。就像是:

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

To abort you can enumerate the dictionary and call Thread.Abort(). Be ready to catch ThreadAbortException

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

回答by Kiril

I asked a similar questions and received a bunch of good answers: Shutting down a multithreaded application

我问了一个类似的问题并得到了很多好的答案:关闭多线程应用程序

Note: my question did not require a graceful exit, but people still recommended that I gracefully exit from the loop of each thread.

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

The main thing to remember is that if you want to avoid having your threads prevent your process from terminating you should set all your threads to background:

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

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

The preferred way to start and manage threads is in a ThreadPool, but just about any container out there can be used to keep a reference to your threads. Your threads should always have a flag that will tell them to terminate and they should continually check it.

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

Furthermore, for better control you can supply your threads with a CountdownLatch: whenever a thread is exiting its loop it will signal on a CountdownLatch. Your main thread will call the CountdownLatch.Wait()method and it will block until all the threads have signaled... this allows you to properly cleanup and ensures that all your threads have shutdown before you start cleaning up.

此外,为了更好的控制,您可以为您的线程提供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();
    }
}

It's also worthy to mention that the Thread.Abort() method does some strange things:

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

When a thread calls Abort on itself, the effect is similar to throwing an exception; the ThreadAbortException happens immediately, and the result is predictable. However, if one thread calls Abort on another thread, the abort interrupts whatever code is running. There is also a chance that a static constructor could be aborted. In rare cases, this might prevent instances of that class from being created in that application domain. In the .NET Framework versions 1.0 and 1.1, there is a chance the thread could abort while a finally block is running, in which case the finally block is aborted.

The thread that calls Abort might block if the thread that is being aborted is in a protected region of code, such as a catch block, finally block, or constrained execution region. If the thread that calls Abort holds a lock that the aborted thread requires, a deadlock can occur.

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

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

回答by Ed Power

As you start each thread, put it's ManagedThreadId into a Dictionary as the key and the thread instance as the value. Use a callback from each thread to return its ManagedThreadId, which you can use to remove the thread from the Dictionary when it terminates. You can also walk the Dictionary to abort threads if needed. Make the threads background threads so that they terminate if your app terminates unexpectedly.

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

You can use a separate callback to signal threads to continue or halt, which reflects a flag set by your UI, for a graceful exit. You should also trap the ThreadAbortException in your threads so that you can do any cleanup if you have to abort threads instead.

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