一次只执行几个线程[重复]
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.Start
、ThreadPool
和Tasks,都给了我完全相同的结果。如果我使用ThreadPool
并在每次线程运行时检查可用线程数,那么总是有 >2000 个可用工作线程和 1000 个可用异步线程。
我只是用上面的代码来测试我们的代码,试图找出发生了什么。在实践中,代码会在所有地方产生线程。该程序以不到 5% 的 CPU 使用率运行,但由于线程执行速度不够快而变得非常缓慢。
【问题讨论】:
but only 3 or 4 at a time.
你的机器有什么确切 CPU?
您能否使用您的应用程序方法运行ThreadPool.GetMaxThreads
并共享此方法返回的值。
您使用QueueUserWorkItem
和Task.Run
有什么特别的原因吗? ***.com/questions/38880743/…
这能回答你的问题吗? ThreadPool not starting new Thread instantly 具体来说,Jim 的 answer。如果您想立即启动线程,请使用 SetMinThreads。
附带说明,someThread
是一个糟糕的方法名称。 Microsoft's guidelines:1。使用动词或动词短语来命名方法。 2. 使用 Pascal 大小写。 DoWork
、ProcessItem
、PutAThreadToSleep
是一些更好的名称示例。
【参考方案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 个WaitCallback
s 大约需要300 毫秒。对Parallel.For
循环执行相同的操作需要大致相同的时间(稍微多一点)。老实说,我不认为这个操作是可并行的。可能在内部需要一个lock
,以确保操作是线程安全的,无论如何都要序列化调用。以上是关于一次只执行几个线程[重复]的主要内容,如果未能解决你的问题,请参考以下文章
java线程只能被启动(Thread.start())一次,那么为啥线程池中的线程能被重复利用呢?