gpt4 book ai didi

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

转载 作者:太空狗 更新时间:2023-10-29 20:42:41 25 4
gpt4 key购买 nike

我在 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.
}

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

最佳答案

在线程上
我会避免自己明确创建线程。
更可取的是使用 ThreadPool.QueueUserWorkItem 或者,如果您可以使用 .Net 4.0,您将获得更强大的 Task parallel library它还允许您以更强大的方式使用 ThreadPool 线程( Task.Factory.StartNew 值得一看)
如果我们选择使用显式创建线程的方法会怎样?
假设您的 list[0].Count 返回 1000 个项目。我们还假设您在高端(在撰写本文时)16 核机器上执行此操作。直接效果是我们有 1000 个线程在争夺这些有限的资源(16 个内核)。
任务数量越大,每个任务运行的时间越长,在 context switching 上花费的时间就越多。 .此外,创建线程的成本很高,如果使用重用现有线程的方法,则可以避免显式创建每个线程的开销。
因此,虽然多线程的最初意图可能是提高速度,但正如我们所见,它可能会产生完全相反的效果。
我们如何克服“过度”线程?
这是 ThreadPool 的地方发挥作用。

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


它们是如何工作的:

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.


所以我们可以看到,通过使用线程池线程,我们两个都更有效率
  • 方面最大化实际工作完成。由于我们没有用线程使处理器过度饱和,因此在线程之间切换花费的时间更少,而实际执行线程应该执行的代码的时间更多。
  • 更快的线程启动 :每个线程池线程都是随时可用的,而不是等待直到构建新线程。
  • 方面最小化内存消耗 ,线程池会将线程数限制为线程池大小,将任何超出线程池大小限制的请求排入队列。 (见 ThreadPool.GetMaxThreads )。这种设计选择背后的主要原因当然是为了我们不会因过多的线程请求而使有限数量的内核过度饱和,从而将上下文切换保持在较低级别。

  • 理论太多了,让我们来测试一下所有这些理论!
    是的,理论上知道这一切很好,但让我们付诸实践,看看会发生什么
    数字告诉我们,应用程序的一个简化的粗略版本可以给我们一个数量级差异的粗略指示。我们将在新线程、线程池和任务并行库(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.
    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();
    }
    结果:
    New Thread Benchmark
    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();
    }
    结果:
    ThreadPool Benchmark
    任务并行库 (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();
    }
    结果:
    Task Parallel Library result
    所以我们可以看到:
    +--------+------------+------------+--------+
    | | new Thread | ThreadPool | TPL |
    +--------+------------+------------+--------+
    | Time | 6749 | 228ms | 222ms |
    | Memory | ≈300kb | ≈103kb | ≈123kb |
    +--------+------------+------------+--------+
    以上内容与我们在理论上的预期非常吻合。与 ThreadPool 相比,新线程的高内存以及较慢的整体性能。 ThreadPool 和 TPL 具有相同的性能,TPL 的内存占用比纯线程池略高,但考虑到任务提供的额外灵活性(例如取消、等待完成、查询任务状态),这可能是值得付出的代价
    在这一点上,我们已经证明使用 ThreadPool 线程在速度和内存方面是更可取的选择。
    不过,我们还没有回答你的问题。如何跟踪正在运行的线程的状态。
    回答你的问题
    鉴于我们收集到的见解,这就是我将如何处理它:
            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
    最后要注意的是,您不能在 Thread.Start 中使用变量 i,因为它会在更改的变量上创建一个闭包,该变量将在所有线程之间有效地共享。为了解决这个问题(假设您需要访问 i),只需创建变量的副本并将副本传入,这将使每个线程一个闭包,从而使其线程安全。
    祝你好运!

    关于c# - 你如何获得 C# 中正在运行的线程列表?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9397729/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com