【问题标题】:How can I run 4 threads each on a different core (parallelism)?如何在不同的核心上运行 4 个线程(并行性)?
【发布时间】:2016-08-15 20:29:10
【问题描述】:

我想并行运行(不并发1四个线程做完全独立的事情。我是并行的新手,我有几个问题。我之所以要这样做是因为性能对我来说真的很重要。我正在使用 4 核 Windows 机器,并且在 Visual Studio Community 2015 中使用 C++。

我应该尝试自己调度线程,以便每个线程在不同的内核上运行,还是应该让操作系统调度程序来执行此操作?在我看来,我认为如果我强制它在不同的核心上运行每个线程会更快。我该怎么做?

这是我迄今为止尝试过的:

#include <thread>
void t1(){//do something}
void t2(){//do something}
void t3(){//do something}
void t4(){//do something}

int main(){
   std::thread thread1(t1);
   std::thread thread2(t2);
   std::thread thread3(t3);
   std::thread thread4(t4);

   t1.join();
   t2.join();
   t3.join();
   t4.join();
}

我知道join() 会阻塞线程直到它完成,但我不确定它是否并行运行线程?我的代码是并发还是并行执行线程?


1注意:

并发本质上是同时执行两个任务。这可能意味着一个在短时间内“暂停”,而另一个正在处理中。

并行性要求在特定时刻至少有两个进程/任务正在积极执行。

【问题讨论】:

  • “同时或并行”
  • 你不能选择在哪个核心线程上运行,这是由操作系统完成的。
  • @Blorgbeard 查看编辑并搜索并发与并行,您会明白我的意思。
  • @doug 并发是操作系统的属性,并行是CPU的属性,这些是不同的东西。 raywenderlich.com/wp-content/uploads/2014/01/…
  • @doug 是的,它们同时运行,但一个可以暂停,而另一个正在运行,然后反过来等等。请看照片raywenderlich.com/wp-content/uploads/2014/03/…

标签: c++ multithreading parallel-processing


【解决方案1】:

您已完成,无需安排任何事情。只要有多个处理器可用,您的线程就会在可用内核上同时运行。

如果可用处理器少于 4 个,例如 2 个,您的线程将以交错方式运行,在任何给定时间最多运行 2 个。


附言亲自体验也很容易 - 只需制作 4 个无限循环并在 4 个不同的线程中运行它们。您将看到正在使用 4 个 CPU。


免责声明:当然,“在幕后”,调度正在由操作系统为您完成。因此,您依赖于 OS 中内置的调度程序的质量来实现并发性。内置在运行 C++ 应用程序的操作系统中的调度程序的公平性超出了 C++ 标准,因此无法保证。但实际上,尤其是在学习编写并发应用程序时,大多数现代操作系统都会在线程调度方面提供足够的公平性。

【讨论】:

  • @rustyn 100% 确定。你是说在我上面写的代码中我并行执行它们(而不是同时看到上面的定义)?谢谢
  • @mata - 只要有 4 个处理器可用,它们就会并行运行。
  • 使用std::async(std::launch::async, t1()); 等。更快。特别是在VS上。 stackoverflow.com/questions/26730287/…
  • @rustyx 太好了!我还有一个关于 join() 的问题。想象一下 4 个线程(相同的工作量):第一个线程:1,2 2nd:3,4 3rd:5,6 4th:7,8 数字(1,2 ...)表示 task1,... 如果我加入他们1、3、5、7 在一个周期内同时执行(在不同的内核上),然后在第二个周期内执行 2、4、6、8。并且 4 个线程同时完成(加上减去几毫秒)我的例子是否正确?如果一个程序的执行在一个线程上需要 16 秒,那么在 4 个线程上大约需要 4 秒(+ 创建线程的时间)
  • @mata - join 的顺序无关紧要。最后,您要等到 所有 线程完成。如果一个线程在你join 时已经完成,join 将立即返回。 p.s.如果您有更多问题,请发布一个新问题,您原来的问题已得到解答,不是吗? :)
【解决方案2】:

没有标准的方法来设置给定线程的亲和性,在后台 std::thread 是使用 linux/unixes 上的 posix 线程和 Windows 下的 windows 线程实现的。解决方案是使用原生 api,例如在 windows 下,下面的代码会导致我的 i7 CPU 的所有 8 个内核被充分利用:

  auto fn = []() {while (true);};
  std::vector<std::thread> at;
  const int num_of_cores = 8;
  for (int n = 0; n < num_of_cores; n++) {
    at.push_back(std::thread(fn));
    // for POSIX: use pthread_setaffinity_np
    BOOL res = SetThreadAffinityMask(at.back().native_handle(), 1u << n);
    assert(res);
  }
  for (auto& t : at) t.join();

但是在注释掉SetThreadAffinityMask之后我仍然得到相同的结果,所有的内核都被充分利用了,所以Windows调度程序做得很好。

如果您想更好地控制系统内核,请查看 OpenMP、TBB(线程构建块)、PPL 等库。按此顺序。

【讨论】:

    【解决方案3】:

    好吧,如果您愿意,您可能想要设置应用程序亲和性。基本上,如果你有类似 i7 CPU 和 4 核/8 线程的东西,如果你为每个核心(而不是每个线程)设置亲和性,你的应用程序会更快。

    有命令行工具可以做到这一点:Set affinity with start /AFFINITY command on Windows 7

    另外,可以通过任务管理器设置亲和力:http://www.windowscentral.com/assign-specific-processor-cores-apps-windows-10

    【讨论】:

    • “如果您将亲和性设置为每个内核一个线程,您的应用会更快”证明?
    • @Slava 因为超线程意味着共享资源 - 缓存、分支预测器等。更少的共享 - 为您的应用程序提供更多资源。好读:agner.org/optimize/blog/read.php?i=6&v=t
    • 更简单和正确的方法是完全禁用超线程。线程亲和力不适合初学者并且经常被滥用(主要是因为很酷的因素)并且可能弊大于利。
    • @Slava Simpler- 是的,正确的- 不确定。如果您使用同一台计算机编译/调试/测试然后运行,那么启用 HT 肯定有助于并行编译:当一些线程等待事件、I/O 等时,其他线程继续工作。是通过 API 在内部设置亲和力还是通过 batch/tm 在外部设置亲和力取决于个人喜好。我个人更喜欢批处理...
    猜你喜欢
    • 2012-10-04
    • 2017-01-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-24
    • 2016-08-28
    • 1970-01-01
    • 2010-12-26
    相关资源
    最近更新 更多