如何在不同的CPU内核上生成线程?

时间:2020-03-05 18:44:23  来源:igfitidea点击:

假设我有一个用C语言编写的程序,该程序的计算量很大,例如将WAV文件列表编码为MP3. 通常,我会一次对文件进行编码,但比方说,我希望程序计算出我拥有多少个CPU内核,并在每个内核上增加一个编码线程。因此,当我在四核CPU上运行该程序时,该程序确定它是四核CPU,计算出有四个内核可以使用,然后产生四个用于编码的线程,每个线程都在各自的单独运行中央处理器。我该怎么做?

如果内核分散在多个物理CPU上,这会有什么不同吗?例如,如果我有一台装有两个四核CPU的机器,是否有任何特殊考虑,或者两个模具中的八个核在Windows中是否被认为是相等的?

解决方案

回答

每个线程通常由操作系统本身处理...因此在一个4核系统上生成4个线程,操作系统将决定在哪个内核上运行,每个内核通常为1个线程。

回答

别那么做。

而是使用线程池。线程池是框架的一种机制(实际上是一个类),我们可以查询新线程。

当我们请求一个新线程时,它将为我们提供一个新线程或者将工作排队,直到释放线程为止。这样,框架负责决定是否应根据当前CPU的数量创建更多线程。

编辑:此外,如前所述,操作系统负责在不同的CPU之间分配线程。

回答

在不同的内核之间拆分线程是操作系统的工作,当线程使用大量CPU时间时,它会自动执行。不用担心至于找出用户有多少个内核,请尝试使用C#中的Environment.ProcessorCount

回答

我们不必担心自己这样做。我有在双四核计算机上运行的多线程.NET应用程序,无论线程是如何启动的(无论是通过ThreadPool还是手动启动),我都能在所有内核上看到均匀的工作分配。

回答

我们不应该(如上所述)尝试自己分配此类内容的原因之一是,我们只是没有足够的信息来正确地进行处理,尤其是在NUMA等方面。

如果我们有一个要运行的线程,并且有一个内核处于空闲状态,则内核将运行线程,请不要担心。

回答

对于托管线程,执行此操作的复杂度要比本地线程高。这是因为CLR线程没有直接绑定到本机OS线程。换句话说,CLR可以根据需要将托管线程从本机线程切换到本机线程。提供函数Thread.BeginThreadAffinity可以将托管线程与本机OS线程锁定在一起。到那时,我们可以尝试使用本机API来赋予基础本机线程处理器亲和力。正如每个人在这里建议的那样,这不是一个好主意。实际上,有文档表明,如果线程仅限于单个处理器或者内核,则可以减少处理时间。

我们还可以浏览System.Diagnostics.Process类。在那里,我们可以找到一个函数来枚举ProcessThread对象作为ProcessThread对象的集合。此类具有设置ProcessorAffinity或者什至设置首选处理器的方法-不确定是什么。

免责声明:我曾经遇到过类似的问题,我认为CPU的利用率不高,并对此进行了大量研究。但是,根据我阅读的所有内容,似乎也不是一个好主意,此处的评论也证明了这一点。但是,它仍然很有趣,并且可以进行实验学习。

回答

它不一定像使用线程池那样简单。

默认情况下,线程池为每个CPU分配多个线程。由于参与我们工作的每个线程都有成本(任务切换开销,CPU的有限L1,L2以及L3高速缓存的使用等),因此,要使用的最佳线程数为<=除非每个线程都从其他计算机请求服务,例如高度可扩展的Web服务,否则可用CPU的数量。在某些情况下,尤其是那些涉及硬盘读写比CPU活动更多的情况,使用1个线程实际上要比使用多个线程更好。

对于大多数应用程序,当然对于WAV和MP3编码,应该将辅助线程的数量限制为可用CPU的数量。这是一些Ccode来查找CPU的数量:

int processors = 1;
string processorsStr = System.Environment.GetEnvironmentVariable("NUMBER_OF_PROCESSORS");
if (processorsStr != null)
    processors = int.Parse(processorsStr);

不幸的是,这并不像限制CPU数量那么简单。我们还必须考虑硬盘控制器和磁盘的性能。

真正找到最佳线程数的唯一方法是尝试错误。当我们使用硬盘,Web服务等时,尤其如此。使用硬盘时,最好不要在四核处理器CPU上同时使用所有四个处理器。另一方面,对于某些Web服务,我们可能最好每个CPU发出10个甚至100个请求。