我一直在读这个主题,对多线程和并行性有点困惑;我读过这个问题
"在多线程系统中,线程是如何分布在CORES之间的?比如说,我有一个创建6个线程的程序。我的系统有3个CORES。在这种情况下,线程会分布在3个CORE之间吗?还是所有线程都在同一个CORE上执行?"-具有3个逻辑核心的物理CPU
这个问题的答案表明,我必须告诉操作系统哪个内核执行什么,在Java或C#这样的语言中进行多线程时,这是一个普遍的事实吗?
问题链接
在一个我没有指定哪个核心做什么的场景中,我可以在用Java或C#等语言编写的多线程程序中实现并行?
我一直在学习一些erlang,我已经达到了生成过程的主题,当这些过程被生成时;erlang是否告诉操作系统将不同的进程分配给不同的内核?我在Learn you some erlang中看到了这段代码,它产生了10个进程,每个进程都会打印出一个数字,但为什么要使用定时器:睡眠?这不只是让程序看起来并行吗?相反,它只是让一些东西停止,以便其他东西可以运行(比如并发)?
4> G = fun(X) -> timer:sleep(10), io:format("~p~n", [X]) end.
#Fun<erl_eval.6.13229925>
5> [spawn(fun() -> G(X) end) || X <- lists:seq(1,10)].
我在java中实现了这一点,并得到了类似的结果,我创建了6个线程,每个"线程"都有一个循环,它打印线程名称,然后打印一个数字,然后休眠10毫秒。
public class ThreadTest {
public static void main(String args[]) {
Thread t1 = new Thread(new Thread2());
Thread t2 = new Thread(new Thread3());
Thread t3 = new Thread(new Thread4());
Thread t4 = new Thread(new Thread5());
Thread t5 = new Thread(new Thread6());
t1.start();
t2.start();
t3.start();
t4.start();
t5.start();
}
}
看看这两个程序,它们是同时执行还是并行执行?我也意识到计算机很快,但即使我不让java程序休眠,它也会一个接一个地打印东西。然而,对于erlang,如果我取消睡眠,它有时会打印出很多数字,然后打印进程的数量并继续计数,或者打印出所有数字,然后进程列表最后一个。
参考上面的问题,java是同时在一个核心上做事情(与上下文切换),还是利用更多的核心并行做事情,但速度太快,无法给我随机结果?(无睡眠)
erlang是否使用了更多的核心并并行地进行工作,因为它有时会在计数过程中打印出进程列表?(无睡眠)
注意:我特意省略了其他线程的代码,只是觉得最好解释一下类的作用
传统的操作系统(OS),例如Linux,管理许多进程的执行(进程本质上对应于程序)。
进程最初在一个线程上执行,但在执行时可以创建其他线程。操作系统的主要任务之一是管理所有进程线程的执行。
- 当只有一个处理器时,操作系统调度程序上下文在不同线程之间切换,以提供并发执行
- 当有多个处理器时,每个处理器基本上都运行OS调度器的一个实例,从而执行等待运行的线程。结果是要执行的线程集的并行执行
语言中并发或线程的行为取决于它的实现方式。
使用Java,JVM的实现很可能会使用操作系统提供的线程机制,即POSIX(请参阅此问题)。因此,多线程Java程序的性能将由操作系统决定。例如,请参阅有关Linux调度程序的详细信息。
对于二郎,情况略有不同,我认为这是混乱的根源。因为Erlang提倡使用大量的进程(即线程),并且这些进程通过消息传递进行通信,所以线程实现必须是高效的。由于这些原因,POSIX线程是不合适的,Erlang虚拟机有自己的线程机制。它的工作方式是为每个具有固定亲缘关系的核心分配一个操作系统线程,并在每个核心上运行Erlang调度器。
操作系统在多核计算机上调度线程的原理与在单核计算机上执行线程的原理没有太大区别。实际上,每个核心都在运行操作系统调度器的副本,这些调度器相互通信,并决定如何最好地将线程分配给核心。如果这些调度器中的一个发现其核心上没有可运行的就绪线程,它将运行在另一个核心中被阻塞的就绪线程。
问题是,这些核心大多是异步的,彼此独立,并且实际上无法保证执行顺序。因此,不同线程输出字符串的顺序是不确定的,每次运行程序时都很容易发生变化。
所有这些都是因为今天的多核计算机实现了符号多处理(SMP)。所有的核心都可以看到所有的内存,所以选择哪个核心是运行线程的最佳核心并没有真正的问题。
然而,我们从Xeons等获得的SMP是"假的",也就是说,它们部分是NUMA,而SMP是由QPI合成的。AMD处理器完全是NUMA,SMP是通过Hypertransport合成的。QPI和HyperTransport都很好,但操作系统不应该忽视这一开销。简而言之,一个内核上的线程访问电子连接到另一个内核的内存需要更长的时间。因此,一个好的操作系统会尝试在离他们访问的内存最近的内核上运行线程,以获得良好的性能。在Xeon领域,这非常复杂,因为机器的默认内存映射在CPU之间交错(试图使伪造的SMP更好)。
因此,一旦你开始深入研究核心亲和力并将线程绑定到特定的核心(如果你真的想的话,操作系统允许你这样做),你的程序就必须完全理解它碰巧运行的硬件的微电子架构。总的来说,这并不完全现实,在几乎所有情况下,最好让操作系统为您解决问题。