时间片调度算法在线程池中

huangapple go评论63阅读模式
英文:

Time slicing algorithm in thread pools

问题

首先,让我们谈谈原始线程。假设我有4个核心和6个线程。这4个核心将会对这6个线程进行“时间分片”。将它们的时间分配给这4个核心。

但是,假设我有一个固定的线程池,有4个线程,我在其中运行了10个线程。我了解到它会将这10个线程视为任务。意味着它不会对这10个线程进行时间分片,而是同时运行4个线程,一旦其中一个完成,就会取出另一个线程继续运行,依此类推。

我有点困惑,为什么在第一种情况下会进行时间分片,而在第二种情况下却不会?但从技术上讲,在第二种情况下也是可能的(比如我有4个核心和一个处理这10个线程的池子)。那么这5个线程将会在这4个核心中进行时间分片。在这5个线程中,最多只能同时有5个任务,直到一个任务完成后才能继续下一个。

我经过深思熟虑写下了这些内容。我在这里说错了什么吗?是否有人能够给出一个基本的经验法则,让我能够在不过度思考的情况下立刻理解这个问题?我感觉我在理解基础知识方面有些遗漏。

英文:

First let's talk about raw threads. Say I have 4 cores and 6 threads. Those 4 cores would 'time-slice' those 6 threads. Giving some time of them all to those 4 cores.

But say I have fixed pool of 4 threads, where I run, say 10 threads. I read that it would 'view' those 10 threads as tasks. Meaning it won't time-slice 10 threads, but run 4 at once, and once one of them finishes, it would take another and so on.

I am a little confused, why in first case there is time slicing and in second case there is not? But technically there could be (say I have 4 cores and pool of 5 that work with those 10 threads).Then those 5 threads would be time sliced in those 4 cores. Also in 5 threads there could me max 5 tasks at the same time, until one finishes it can take next.

I wrote this with a lot of thinking. Did I say something wrong here? And can someone make a basic rule of thumb of how I can understand this instantly without overthinking? I feel like I am missing basics.

答案1

得分: 1

在第一种情况下(4个核心和6个线程),4个线程将同时运行。2个线程将处于等待状态。
例如,如果线程1、2、3、4正在运行,而5、6正在等待,则在一段时间后,线程1、3、5、6可能会在运行,而2、4正在等待。
这就是时间片轮转的含义。

在第二种情况下,当你有一个包含4个工作线程的线程池时,同样的情况也会发生。
假设你提交了10个任务(Runnables),那么其中的4个任务肯定会同时运行;另外6个将在线程池队列中等待。
每当一个任务完成时,线程将从队列中取出另一个任务。
这将持续发生,直到队列中没有更多的任务为止。
请注意,即使队列中没有任务,这4个工作线程仍然保持活动状态,尽管处于休眠状态(操作系统将运行来自其他进程的线程)。

如果所有任务都是100% CPU密集型,那么这种线程池设计更好,因为如果操作系统只有4个核心,那么即使有多个任务等待处理,超过4个线程也是无用的。
通常情况下,在同一系统中运行大量线程是低效的,因为上下文切换是昂贵且缓慢的。

英文:

In the first case (4 cores and 6 threads), 4 threads would run at the same time. 2 threads will be waiting.
For instance, if threads 1, 2, 3, 4 are running and 5, 6 are waiting, then its possible that after some time, threads 1, 3, 5, 6 are running and 2, 4 are waiting.
This is what time-slicing means.

In the second case, when you have a thread pool of 4 worker threads, again the same thing will happen.
Suppose you submitted 10 tasks(Runnables), then 4 of those tasks would definitely run at once; 6 will be waiting in the threadpool queue.
Whenever a task is done, the thread will pull another task from the queue.
This will keep on happening until there are no more tasks left in the queue.
Note that even when there are no tasks in the queue, the 4 worker threads are still alive, although in sleeping state (OS will run threads from other processes).

This thread pool design is better if all the tasks are 100% CPU-bound because if the OS has only 4 cores, then having more than 4 threads is useless, no matter how many tasks are pending.
In general, its not efficient to have a lot of threads running in the same system, as context switching is expensive and slow.

答案2

得分: 1

> 我有一个固定的4线程池,在其中运行10个线程。

通常情况下,不建议将Thread对象提交给ExecutorService,因为会导致像这样的混淆。

之所以可以将Thread对象提交给ExecutorService,是因为Thread类实现了Runnable接口。ExecutorService将会像对待其他实现了Runnable接口的对象一样来处理Thread对象:

当你将Thread对象提交给线程池时,它会被添加到线程池的工作队列中。然后线程池中的某个线程会提取该对象,并直接调用Thread.run()方法,就像对待其他任何实现了Runnable接口的对象一样。

因此,Thread对象的run()方法会在线程池的一个线程中运行,而不是在自己单独的新线程中运行。Thread.start()方法不会被调用来启动新线程。

以下两种写法是等价的:

threadPool.submit(new Thread() {
    public void run() {
        doSomething();
    }
});

threadPool.submit(new Runnable() {
    public void run() {
        doSomething();
    }
});
英文:

> I have fixed pool of 4 threads, where I run, say 10 threads.

Normally you don't submit Thread objects to an ExecutorService because it creates confusion like this.

Submitting a Thread object to an ExecutorService is only possible thanks to the fact that the Thread class implements Runnable. The ExecutorService will treat the Thread object the same as any other implementation of Runnable:

When you submit a Thread object to a thread pool, it's added to the pool's work queue. One the threads in the pool then picks it up, and calls the Thread.run() method directly, like it would for any other instance of Runnable.

The run() method of the Thread object therefore runs in one of the thread pool's threads, and not in a new thread of its own. The Thread.start() method is never called to start a new thread.

These two are equivalent:

threadPool.submit(new Thread() {
    public void run() {
        doSomething();
    }
});

threadPool.submit(new Runnable() {
    public void run() {
        doSomething();
    }
});

huangapple
  • 本文由 发表于 2020年8月16日 19:12:16
  • 转载请务必保留本文链接:https://go.coder-hub.com/63436154.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定