【问题标题】:How to ensure that std::thread are created in multi core?如何确保在多核中创建 std::thread?
【发布时间】:2013-04-04 03:54:14
【问题描述】:

我正在使用 Visual Studio 2012。我有一个模块,在通过 xml 遍历它们的相应路径后,我必须从硬盘读取大量文件。为此我正在做

std::vector<std::thread> m_ThreadList;

在一个while循环中,我将一个新线程推回到这个向量中,类似于

m_ThreadList.push_back(std::thread(&MyClass::Readfile, &MyClassObject, filepath,std::ref(polygon)));

我的 C++11 多线程知识有限。我在这里遇到的问题是,如何在特定内核上创建线程?我知道vs2012中的parallel_for和parallel_for_each,它们可以充分利用内核。但是,有没有办法使用标准 C++11 做到这一点?

【问题讨论】:

  • C++ 线程函数不知道“核心”,因此您无法将线程绑定到特定核心。
  • 作为一般提示,如果您使用多个线程从同一文件系统上的多个文件中读取,您很可能会使程序变慢,因为操作系统具有每次有线程上下文切换时来回寻找。 (至少在旧的机械硬盘上,现代 SSD 应该能更好地处理这个问题。)
  • 如果这真的只是关于“我不希望我的 4 个线程最终都在同一个内核上”。那么简单的答案是“拜托,您使用的操作系统完全了解多核处理器,而且还没有那么笨”。但是,如果问题是 “我希望线程 2 在核心 3 上运行”(无论出于何种原因,这对于 “正常” 应用程序中的多核来说都是必要的),那么您就可以受平台相关功能的支配了。
  • 顺便说一句,如果你有 100 个文件,你会启动 100 个线程吗?祝你好运。

标签: c++ multithreading concurrency c++11 visual-studio-2012


【解决方案1】:

正如其他 cmets 所指出的,您不能在“特定核心”上创建线程,因为 C++ 不了解此类架构细节。此外,在大多数情况下,操作系统将能够很好地管理内核/处理器之间的线程分布。

也就是说,在某些情况下,强制在内核之间分配特定的线程可能对性能有益。例如,通过强制一个线程在一个特定的内核上执行,可以最大限度地减少不同处理器缓存之间的数据移动(这对于某些内存受限场景中的性能至关重要)。

如果您想走这条路,您将不得不研究特定于平台的例程。例如,对于具有 POSIX 线程的 GNU/linux,您将需要 pthread_setaffinity_np()、FreeBSD 中的 cpuset_setaffinity()、Windows 中的 SetThreadAffinityMask() 等。

如果你有兴趣,我这里有一些相关的代码sn-ps:

http://gitorious.org/piranhapp0x/mainline/blobs/master/src/thread_management.cpp

【讨论】:

【解决方案2】:

我相当肯定核心亲和力不包含在 std::thread 中。假设是操作系统完全有能力充分利用可用的内核。除了最极端的情况外,您不会违背操作系统的决定,所以这个假设是公平的。

如果您确实走这条路,那么您必须在代码中添加一些决策以考虑机器架构,以确保您的决策比您运行的每台机器上的操作系统更好。这需要很大的努力!对于初学者,您将希望限制线程数以匹配计算机上的内核数。而且您对机器中发生的其他事情一无所知;操作系统可以!

这就是存在线程池的原因。默认情况下,它们倾向于拥有与内核一样多的线程,由语言运行时自动设置。 AFAIK C++11 没有其中之一。因此,要获得最佳性能,您可以做的一件好事是找出有多少内核并将您拥有的线程数限制在该数量。否则最好相信操作系统。

Joachim Pileborg 的评论非常值得关注,除非每个线程完成的工作超过了 I/O 开销。

【讨论】:

  • 可以吗,如果在填充向量后,我运行 parallel_for_each 循环来加入线程?
  • @Atul:几乎可以肯定不是。通常,您在 parallel_for_each(或 parallel_for)的 function_object 中执行的操作不应与其他线程执行同步操作。事实上,如果您自己创建任何线程,parallel_for 和 parallel_for_each 通常会变得相当混乱(表现得更糟)。
  • ...另一方面,您可以考虑只制作一个对象向量(包含您需要处理的文件路径),然后在向量上使用 parallel_for_each。 parallel_for_each 经过精心设计,以使用具有最佳线程数和精心设计的亲和性优化的线程池。
【解决方案3】:

在将线程分派到内核的上下文中快速概述线程:

大多数现代操作系统都使用内核级线程或混合线程。使用内核级线程,操作系统“看到”每个进程中的所有线程;与 Java 中使用的用户级线程相反,在 Java 中,操作系统只看到一个进程,并且不了解线程。现在,由于使用内核级线程,操作系统可以识别进程的单独线程,并管理它们到给定内核的分派,因此有可能实现真正的并行性——同一进程的多个线程在不同的内核上运行。但是,作为程序员,您在使用std::thread 时将无法控制这一点;操作系统决定。对于用户级线程,所有线程的管理都是在用户级完成的,对于 Java,一个库管理“调度”。在混合线程的情况下,使用内核线程,其中每个内核线程实际上是一组用户级线程。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-05-06
    • 2023-03-03
    • 2012-04-14
    • 1970-01-01
    • 2014-08-06
    • 1970-01-01
    • 2020-04-12
    • 2021-11-11
    相关资源
    最近更新 更多