多核处理器上的多线程,并发和并行

时间:2013-12-27 08:18:05

标签: multithreading parallel-processing erlang multicore

我一直在阅读有关该主题的内容,我对多线程和并行性感到有些困惑;我读过这个问题

“如何在多线程系统中的CORES之间分配线程?让我说我有一个创建6个线程的程序。我的系统有3个CORES。在这种情况下,线程将分布在3个CORE之间或者所有线程都会执行在同一个核心?“ - 具有3个逻辑核心的物理CPU

这个问题的答案表明,我必须告诉操作系统哪个核心执行什么,这在Java或C#等语言中进行多线程时是否是普遍的事实?

Link to question

在我没有指定哪个核心可以实现的情况下,我可以在用Java或C#等语言编写的多线程程序中实现并行性?

我一直在学习一些erlang,并且当这些过程产生时,我已经达到了产卵过程的主题; erlang告诉操作系统将不同的进程分配给不同的核心吗? 我从Learn you some erlang看到了这个代码,这会生成10个进程,每个进程都会打印出一个数字,但为什么要使用timer:sleep?是不是只是让程序看起来并行而不是它只是让某些东西停止而其他东西可以运行(比如并发)?

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是否使用更多核心并且并行处理,因为它有时会在计数过程中打印出进程列表? (没有睡觉)

注意:我故意遗漏了其他线程的代码,并且认为最好解释这些类的功能。

2 个答案:

答案 0 :(得分:3)

传统的操作系统(OS),例如Linux,管理许多进程的执行(进程基本上对应于程序)。

进程最初在一个线程上执行,但可以在执行时创建其他线程。操作系统的主要任务之一是管理所有进程线程的执行。

  • 当只有一个处理器时,OS调度程序上下文在不同的线程之间切换,以提供并发执行
  • 当存在多个处理器时,每个处理器实质上运行OS调度程序的实例,从而执行等待运行的线程。结果是要执行的线程集的并行执行

语言中并发或线程的行为取决于它的实现方式。

使用Java,JVM的实现很可能使用操作系统提供的线程机制,即POSIX(请参阅this问题)。因此,多线程Java程序的性能将由OS确定。例如,请参阅details on the Linux scheduler

使用Erlang,情况略有不同,我认为是混乱的根源。因为Erlang主张使用大量进程(即线程)并且这些进程与消息传递进行通信,所以线程实现必须是高效的。由于这些原因,POSIX线程不适合,Erlang虚拟机有自己的线程机制。这种方式的工作方式是为每个核心分配一个具有固定亲和力的OS线程,并在每个核心上运行Erlang调度程序。

答案 1 :(得分:2)

操作系统如何在多核计算机上调度线程的原则与在单个核心机器上完成的操作方式没有太大差别。实际上,每个核心都在运行OS'es调度程序的副本,这些调度程序相互通信并决定如何最好地将线程分配给核心。如果其中一个调度程序发现它没有准备就绪的线程在其核心上运行,那么它将运行一个在另一个核心中被阻塞的就绪线程。

问题是这些核心大多是异步的并且彼此独立,并且实际上不能保证执行顺序。因此,不同线程输出字符串的顺序不是确定性的,并且每次运行程序时都很容易区分。

所有这一切都有效,因为今天的多核计算机实现了Symetrical Multi Processing(SMP)。所有内核都可以看到所有内存,因此选择哪个内核是运行线程的最佳内核没有任何实际问题。

然而,我们从Xeons等获得的SMP是'假的',即它们部分是NUMA,由QPI合成SMP。 AMD处理器完全是NUMA,SMP通过Hypertransport合成。 QPI和HyperTransport非常好,但操作系统不应忽略开销。简而言之,一个内核上的线程需要更长的时间才能访问以电子方式连接到不同内核的内存。因此,一个好的操作系统将尝试在最接近他们访问的内存的内核上运行线程,以获得良好的性能。在Xeon领域,这很复杂,因为机器的默认存储器映射在CPU之间交错(试图使伪造的SMP更好)。

因此,只要您开始深入研究核心关联点并将线程绑定到特定内核(操作系统允许您执行的操作,您的程序必须完成),您的程序必须完全理解硬件的微电子架构,它恰好发现自己继续前进。总的来说,这并不完全是现实的,几乎在所有情况下,最好让操作系统为你排序。