一次只执行几个线程[重复]

Posted

技术标签:

【中文标题】一次只执行几个线程[重复]【英文标题】:Only a few threads executing at a time [duplicate] 【发布时间】:2021-10-13 14:07:27 【问题描述】:

我正在开发一个程序,在该程序中我们不断启动新线程以启动并完成一项工作。我们注意到,即使我们可能启动了 10 个线程,一次也只有 3 或 4 个线程在执行。为了测试它,我做了一个这样的基本示例:

private void startThreads()

    for (int i = 0; i < 100; i++)
    
         //Task.Run(() => someThread());
                            
         //Thread t = new Thread(() => someThread());
         //t.Start();
                            
         ThreadPool.QueueUserWorkItem(someThread);
    


private void someThread()

    Thread.Sleep(1000);

简单的东西对吗?好吧,代码创建了 100 个线程,它们开始执行……但一次只有 3 或 4 个。当它们完成时,下一个线程开始执行。我本来预计几乎​​所有这些都同时开始执行。对于 100 个线程(每个线程有 1 秒的睡眠时间),所有线程大约需要 30 秒才能完成。我原以为它会花费比这更少的时间。

我尝试过使用Thread.StartThreadPool 和Tasks,都给了我完全相同的结果。如果我使用ThreadPool 并在每次线程运行时检查可用线程数,那么总是有 >2000 个可用工作线程和 1000 个可用异步线程。

我只是用上面的代码来测试我们的代码,试图找出发生了什么。在实践中,代码会在所有地方产生线程。该程序以不到 5% 的 CPU 使用率运行,但由于线程执行速度不够快而变得非常缓慢。

【问题讨论】:

but only 3 or 4 at a time. 你的机器有什么确切 CPU? 您能否使用您的应用程序方法运行ThreadPool.GetMaxThreads 并共享此方法返回的值。 您使用QueueUserWorkItemTask.Run 有什么特别的原因吗? ***.com/questions/38880743/… 这能回答你的问题吗? ThreadPool not starting new Thread instantly 具体来说,Jim 的 answer。如果您想立即启动线程,请使用 SetMinThreads。 附带说明,someThread 是一个糟糕的方法名称。 Microsoft's guidelines:1。使用动词或动词短语来命名方法。 2. 使用 Pascal 大小写。 DoWorkProcessItemPutAThreadToSleep 是一些更好的名称示例。 【参考方案1】:

是的,您可能只有几个线程同时运行。这就是ThreadPool 的工作原理。它不一定同时运行所有线程。它会快速将它们排队,然后在每个线程运行时将其留给ThreadPool 处理。

如果您想确保所有 100 个线程同时运行,您可以使用:

ThreadPool.SetMinThreads(100, 100);

例如看下面的代码,这是没有线程池最小大小的结果:

没有最小线程

internal void startThreads()
    
        ThreadPool.GetMaxThreads(out int maxThread, out int completionPortThreads);

        stopwatch.Start();
        var result = Parallel.For(0, 20, (i) =>
        
            ThreadPool.QueueUserWorkItem(someThread, i);
        );

        while (!result.IsCompleted)  
        Console.WriteLine("Queueing completed...");
    

    private void someThread(Object stateInfo)
    
        int threadNum = (int)stateInfo;
        Console.WriteLine(threadNum + " started.");
        Thread.Sleep(10);
        Console.WriteLine(threadNum + " finnished.");
    

结果(无 MinThreads)

9 started.
7 started.
11 started.
10 started.
1 finnished.
12 started.
9 finnished.
13 started.
2 finnished.
4 finnished.
15 started.
3 finnished.
8 finnished.
16 started.
10 finnished.
6 finnished.
19 started.
0 finnished.
14 started.
5 finnished.
7 finnished.
17 started.
18 started.
11 finnished.

使用 MinThreads

internal void startThreads()
    
        ThreadPool.GetMaxThreads(out int maxThread, out int completionPortThreads);
        ThreadPool.SetMinThreads(20, 20); // HERE <-------

        stopwatch.Start();
        var result = Parallel.For(0, 20, (i) =>
        
            ThreadPool.QueueUserWorkItem(someThread, i);
        );

        while (!result.IsCompleted)  
        Console.WriteLine("Queueing completed...");
    

结果

...
7 started.
15 started.
9 started.
12 started.
17 started.
13 started.
16 started.
19 started.
18 started.
5 finnished.
3 finnished.
4 finnished.
6 finnished.
0 finnished.
14 finnished.
1 finnished.
10 finnished.
...

一个不错的干净设计。

【讨论】:

使用并行循环在ThreadPool 上安排工作的原因是什么? OP 使用简单的for 循环来安排工作。你认为你的方法有一些优势吗? @TheodorZoulias,它可以更快地询问他们。在程序的上下文中它没有优势,而是强调即使任务同时排队,它们也不会同时运行。 我只是做了一个粗略的性能测试。在我的机器上使用简单的for 循环使用ThreadPool.QueueUserWorkItem 调度1,000,000 个WaitCallbacks 大约需要300 毫秒。对Parallel.For 循环执行相同的操作需要大致相同的时间(稍微多一点)。老实说,我不认为这个操作是可并行的。可能在内部需要一个lock,以确保操作是线程安全的,无论如何都要序列化调用。

以上是关于一次只执行几个线程[重复]的主要内容,如果未能解决你的问题,请参考以下文章

一定间隔时间下重复执行一个函数的几个方法

线程重复执行问题与线程池

java线程只能被启动(Thread.start())一次,那么为啥线程池中的线程能被重复利用呢?

多线程问题(算法高阶多线程算法)存在重复元素 II(数组哈希表)计数质数(数组数学)

在我的应用程序中禁用多指触摸[重复]

防止线程并发导致事务的重复执行