你如何获得 C# 中正在运行的线程列表?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/9397729/
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-09 07:12:27  来源:igfitidea点击:

How do you get list of running threads in C#?

c#multithreadingthreadpool

提问by Shiv Kumar Sah

I create dynamic threads in C# and I need to get the status of those running threads.

我在 C# 中创建动态线程,我需要获取那些正在运行的线程的状态。

List<string>[] list;
list = dbConnect.Select();

for (int i = 0; i < list[0].Count; i++)
{
    Thread th = new Thread(() =>{
        sendMessage(list[0]['1']);
        //calling callback function
    });
    th.Name = "SID"+i;
    th.Start();
}

for (int i = 0; i < list[0].Count; i++)
{
    // here how can i get list of running thread here.
}

How can you get list of running threads?

如何获取正在运行的线程列表?

采纳答案by Scott Chamberlain

Create a List<Thread>and store each new thread in your first for loop in it.

创建一个List<Thread>并将每个新线程存储在您的第一个 for 循环中。

List<string>[] list;
List<Thread> threads = new List<Thread>();
list = dbConnect.Select();

for (int i = 0; i < list[0].Count; i++)
{
    Thread th = new Thread(() =>{
        sendMessage(list[0]['1']);
        //calling callback function
    });
    th.Name = "SID"+i;
    th.Start();
    threads.add(th)
}

for (int i = 0; i < list[0].Count; i++)
{
    threads[i].DoStuff()
}

However if you don't need iyou can make the second loop a foreachinstead of a for

但是,如果您不需要,i您可以使第二个循环 aforeach而不是 afor



As a side note, if your sendMessagefunction does not take very long to execute you should somthing lighter weight then a full Thread, use a ThreadPool.QueueUserWorkItemor if it is available to you, a Task

附带说明一下,如果您的sendMessage函数执行时间不长,您应该比完整的线程轻一些,使用ThreadPool.QueueUserWorkItem或者如果它对您可用,则使用Task

回答by Stefan Paul Noack

Process.GetCurrentProcess().Threads

This gives you a list of all threads running in the current process, but beware that there are threads other than those you started yourself.

这将为您提供当前进程中运行的所有线程的列表,但请注意,除了您自己启动的线程之外,还有其他线程。

回答by BlueM

Use Process.Threadsto iterate through your threads.

使用Process.Threads通过你的线程进行迭代。

回答by jason

Use Process.Threads:

使用Process.Threads

var currentProcess = Process.GetCurrentProcess();
var threads = currentProcess.Threads;

Note: anythreads owned by the current process will show up here, including those not explicitly created by you.

注意:当前进程拥有的任何线程都将显示在此处,包括您未明确创建的线程。

If you only want the threads that you created, well, why don't you just keep track of them when you create them?

如果您只想要您创建的线程,那么为什么不在创建它们时跟踪它们呢?

回答by Anastasiosyal

On Threads

在线程上

I would avoid explicitly creating threads on your own.

我会避免自己明确创建线程。

It is much more preferable to use the ThreadPool.QueueUserWorkItemor if you do can use .Net 4.0 you get the much more powerful Task parallel librarywhich also allows you to use a ThreadPool threads in a much more powerful way (Task.Factory.StartNewis worth a look)

最好使用ThreadPool.QueueUserWorkItem或者如果你可以使用 .Net 4.0 你会得到更强大的Task 并行库,它也允许你以更强大的方式使用 ThreadPool 线程(Task.Factory.StartNew值得一看)

What if we choose to go by the approach of explicitly creating threads?

如果我们选择使用显式创建线程的方法会怎样?

Let's suppose that your list[0].Count returns 1000 items. Let's also assume that you are performing this on a high-end (at the time of this writing) 16core machine. The immediate effect is that we have 1000 threads competingfor these limited resources (the 16 cores).

假设您的 list[0].Count 返回 1000 个项目。我们还假设您在高端(在撰写本文时)16 核机器上执行此操作。直接效果是我们有 1000 个线程在争夺这些有限的资源(16 个内核)。

The larger the number of tasks and the longer each of them runs, the more time will be spent in context switching. In addition, creating threads is expensive, this overhead creating each thread explicitly could be avoided if an approach of reusing existing threads is used.

任务数量越多,每个任务运行的时间越长,在上下文切换上花费的时间就越多。此外,创建线程的成本很高,如果使用重用现有线程的方法,则可以避免显式创建每个线程的开销。

So while the initial intent of multithreading may be to increasespeed, as we can see it can have quite the oppositeeffect.

因此,虽然多线程的最初意图可能是提高速度,但正如我们所见,它可能会产生完全相反的效果。

How do we overcome 'over'-threading?

我们如何克服“过度”线程?

This is where the ThreadPoolcomes into play.

这就是ThreadPool发挥作用的地方。

A thread pool is a collection of threads that can be used to perform a number of tasks in the background.

线程池是可用于在后台执行多个任务的线程集合。

How do they work:

它们是如何工作的:

Once a thread in the pool completes its task, it is returned to a queue of waiting threads, where it can be reused. This reuse enables applications to avoid the cost of creating a new thread for each task.

Thread pools typically have a maximum number of threads. If all the threads are busy, additional tasks are placed in queue until they can be serviced as threads become available.

一旦池中的线程完成其任务,它就会返回到等待线程的队列中,在那里它可以被重用。这种重用使应用程序能够避免为每个任务创建新线程的成本。

线程池通常具有最大线程数。如果所有线程都忙,则将其他任务放入队列中,直到线程可用时才能为它们提供服务。

So we can see that by using a thread pool threads we are more efficient both

所以我们可以看到,通过使用线程池线程,我们两个都更有效率

  • in terms of maximizing the actual workgetting done. Since we are not over saturating the processors with threads, less time is spent switching between threads and more time actually executing the code that a thread is supposed to do.
  • Faster thread startup: Each threadpool thread is readily available as opposed to waiting until a new thread gets constructed.
  • in terms of minimising memory consumption, the threadpool will limit the number of threads to the threadpool size enqueuing any requests that are beyond the threadpool size limit. (see ThreadPool.GetMaxThreads). The primary reason behind this design choice, is of course so that we don't over-saturate the limited number of cores with too many thread requests keeping context switching to lower levels.
  • 最大限度地完成实际工作方面。由于我们没有用线程使处理器过度饱和,因此在线程之间切换花费的时间更少,而实际执行线程应该执行的代码的时间更多。
  • 更快的线程启动:每个线程池线程随时可用,而不是等待新线程构建完成。
  • 最小化内存消耗方面,线程池将线程数限制为线程池大小,将任何超出线程池大小限制的请求排入队列。(见ThreadPool.GetMaxThreads)。这种设计选择背后的主要原因当然是为了我们不会因过多的线程请求而使有限数量的内核过度饱和,从而将上下文切换保持在较低级别。

Too much Theory, let's put all this theory to the test!

理论太多了,让我们来测试一下所有这些理论!

Right, it's nice to know all this in theory, but let's put it to practice and see what the numbers tell us, with a simplified crude version of the application that can give us a coarse indication of the difference in orders of magnitude. We will do a comparison between new Thread, ThreadPool and Task Parallel Library (TPL)

是的,理论上知道所有这些很好,但是让我们将其付诸实践,看看数字告诉我们什么,使用应用程序的简化原始版本,可以粗略地指示数量级的差异。我们将在新线程、线程池和任务并行库(TPL)之间做一个比较

new Thread

新线程

    static void Main(string[] args)
    {
        int itemCount = 1000;

        Stopwatch stopwatch = new Stopwatch(); 
        long initialMemoryFootPrint = GC.GetTotalMemory(true);

        stopwatch.Start();
        for (int i = 0; i < itemCount; i++)
        {
            int iCopy = i;  // You should not use 'i' directly in the thread start as it creates a closure over a changing value which is not thread safe. You should create a copy that will be used for that specific variable.
            Thread thread = new Thread(() =>
            {
                // lets simulate something that takes a while
                int k = 0;
                while (true)
                {
                    if (k++ > 100000)
                        break;
                }

                if ((iCopy + 1) % 200 == 0) // By the way, what does your sendMessage(list[0]['1']); mean? what is this '1'? if it is i you are not thread safe.
                    Console.WriteLine(iCopy + " - Time elapsed: (ms)" + stopwatch.ElapsedMilliseconds);
            });

            thread.Name = "SID" + iCopy; // you can also use i here. 
            thread.Start();
        }

        Console.ReadKey();
        Console.WriteLine(GC.GetTotalMemory(false) - initialMemoryFootPrint);
        Console.ReadKey();
    }

Result:

结果:

New Thread Benchmark

新线程基准

ThreadPool.EnqueueUserWorkItem

ThreadPool.EnqueueUserWorkItem

    static void Main(string[] args)
    {
        int itemCount = 1000;

        Stopwatch stopwatch = new Stopwatch(); 
        long initialMemoryFootPrint = GC.GetTotalMemory(true);

        stopwatch.Start();

        for (int i = 0; i < itemCount; i++)
        {
            int iCopy = i; // You should not use 'i' directly in the thread start as it creates a closure over a changing value which is not thread safe. You should create a copy that will be used for that specific variable.
            ThreadPool.QueueUserWorkItem((w) =>
            {
                // lets simulate something that takes a while
                int k = 0;
                while (true)
                {
                    if (k++ > 100000)
                        break;
                }

                if ((iCopy + 1) % 200 == 0) 
                    Console.WriteLine(iCopy + " - Time elapsed: (ms)" + stopwatch.ElapsedMilliseconds);
            });
        }

        Console.ReadKey();
        Console.WriteLine("Memory usage: " + (GC.GetTotalMemory(false) - initialMemoryFootPrint));
        Console.ReadKey();
    }

Result:

结果:

ThreadPool Benchmark

线程池基准

Task Parallel Library (TPL)

任务并行库 (TPL)

    static void Main(string[] args)
    {
        int itemCount = 1000;

        Stopwatch stopwatch = new Stopwatch(); 
        long initialMemoryFootPrint = GC.GetTotalMemory(true);

        stopwatch.Start();
        for (int i = 0; i < itemCount; i++)
        {
            int iCopy = i;  // You should not use 'i' directly in the thread start as it creates a closure over a changing value which is not thread safe. You should create a copy that will be used for that specific variable.
            Task.Factory.StartNew(() =>
            {
                // lets simulate something that takes a while
                int k = 0;
                while (true)
                {
                    if (k++ > 100000)
                        break;
                }

                if ((iCopy + 1) % 200 == 0) // By the way, what does your sendMessage(list[0]['1']); mean? what is this '1'? if it is i you are not thread safe.
                    Console.WriteLine(iCopy + " - Time elapsed: (ms)" + stopwatch.ElapsedMilliseconds);
            });
        }

        Console.ReadKey();
        Console.WriteLine("Memory usage: " + (GC.GetTotalMemory(false) - initialMemoryFootPrint));
        Console.ReadKey();
    }

Result:

结果:

Task Parallel Library result

任务并行库结果

So we can see that:

所以我们可以看到:

+--------+------------+------------+--------+
|        | new Thread | ThreadPool |  TPL   |
+--------+------------+------------+--------+
| Time   | 6749       | 228ms      | 222ms  |
| Memory | ≈300kb     | ≈103kb     | ≈123kb |
+--------+------------+------------+--------+

The above falls nicely inline to what we anticipated in theory. High memory for new Thread as well as slower overall performance when compared to ThreadPool. ThreadPool and TPL have equivalent performance with TPL having a slightly higher memory footprint than a pure thread pool but it's probably a price worth paying given the added flexibility Tasks provide (such as cancellation, waiting for completion querying status of task)

以上内容与我们在理论上的预期非常吻合。与 ThreadPool 相比,新线程的高内存以及较慢的整体性能。ThreadPool 和 TPL 具有相同的性能,TPL 的内存占用比纯线程池略高,但考虑到任务提供的额外灵活性(例如取消、等待完成查询任务状态),这可能是值得付出的代价

At this point, we have proven that using ThreadPool threads is the preferable option in terms of speed and memory.

在这一点上,我们已经证明使用 ThreadPool 线程在速度和内存方面是更可取的选择。

Still, we have not answered your question. How to track the state of the threads running.

不过,我们还没有回答你的问题。如何跟踪正在运行的线程的状态。

To answer your question

回答你的问题

Given the insights we have gathered, this is how I would approach it:

鉴于我们收集到的见解,这就是我将如何处理它:

        List<string>[] list = listdbConnect.Select()
        int itemCount = list[0].Count;
        Task[] tasks = new Task[itemCount];
        stopwatch.Start();
        for (int i = 0; i < itemCount; i++)
        {
            tasks[i] = Task.Factory.StartNew(() =>
            {
                // NOTE: Do not use i in here as it is not thread safe to do so! 
                sendMessage(list[0]['1']);
                //calling callback function
            });
        }

        // if required you can wait for all tasks to complete
        Task.WaitAll(tasks);

        // or for any task you can check its state with properties such as: 
        tasks[1].IsCanceled
        tasks[1].IsCompleted
        tasks[1].IsFaulted 
        tasks[1].Status

As a final note, you can notuse the variable i in your Thread.Start, since it would create a closure over a changing variable which would effectively be shared amongst all Threads. To get around this (assuming you need to access i), simply create a copy of the variable and pass the copy in, this would make one closure per thread which would make it thread safe.

最后要注意的是,您不能在 Thread.Start 中使用变量 i,因为它会在更改的变量上创建一个闭包,该变量将在所有线程之间有效地共享。为了解决这个问题(假设您需要访问 i),只需创建变量的副本并将副本传入,这将使每个线程一个闭包,从而使其线程安全。

Good luck!

祝你好运!