如何在不同的CPU内核上生成线程?
比方说,我有一个在C#中的程序,做了一些昂贵的计算,比如将一个WAV文件编码成MP3。 通常我会一次对一个文件进行编码,但是假设我想让程序找出我有多less个CPU核心,并在每个核心上启动一个编码线程。 所以,当我在一个四核CPU上运行这个程序时,程序会发现它是一个四核心的CPU,它指出有四个核心可以工作,然后产生四个线程用于编码,每个线程独立运行中央处理器。 我将如何做到这一点?
如果核心分布在多个物理CPU上,会有什么不同? 如果我有一台带有两个四核CPU的机器,有没有特别的考虑,或者在Windows中,两个模具的八个核心是否相同?
不要打扰这样做。
而是使用线程池 。 线程池是您可以查询新线程的框架机制(实际上是一个类)。
当你要求一个新的线程时,它会给你一个新的线程或排队的工作,直到一个线程被释放。 这样框架负责决定是否应该创build更多的线程,取决于当前CPU的数量。
编辑:另外,正如已经提到的那样,OS负责在不同的CPU之间分配线程。
这并不像使用线程池那么简单。
默认情况下,线程池为每个CPU分配多个线程。 因为涉及到你所做的工作的每个线程都有成本(任务切换开销,使用CPU非常有限的L1,L2和L3caching等等),所使用的最佳线程数是<=可用CPU的数量 – 除非每个线程正在从其他机器请求服务 – 例如高度可扩展的Web服务。 在某些情况下,特别是涉及比CPU活动更多的硬盘读写操作的情况下,实际上最好使用1个线程而不是多个线程。
对于大多数应用程序,当然对于WAV和MP3编码,您应该将工作线程的数量限制为可用CPU的数量。 这里有一些C#代码来查找CPU的数量:
int processors = 1; string processorsStr = System.Environment.GetEnvironmentVariable("NUMBER_OF_PROCESSORS"); if (processorsStr != null) processors = int.Parse(processorsStr);
不幸的是,这并不像限制CPU的数量那么简单。 您还必须考虑到硬盘控制器和磁盘的性能。
你真的可以find最佳的线程数的唯一方法是试用一个错误。 当您使用硬盘,Web服务等时尤其如此。 对于硬盘,最好不要在四核处理器CPU上使用全部四个处理器。 另一方面,对于某些Web服务,每个CPU可能要做10个甚至100个请求。
在托pipe线程的情况下,这样做的复杂度比本地线程的复杂程度要大。 这是因为CLR线程不直接绑定到本地操作系统线程。 换句话说,CLR可以将一个托pipe线程从本地线程切换到本地线程,因为它认为合适。 Thread.BeginThreadAffinity函数提供了一个与本地操作系统线程locking的托pipe线程。 在这一点上,您可以尝试使用本机API来提供底层的本地线程处理器关联。 正如大家在这里所暗示的,这不是一个好主意。 事实上,有文件表明如果线程限于单个处理器或内核,线程可以获得较less的处理时间。
您还可以浏览System.Diagnostics.Process类。 在那里你可以find一个函数来枚举一个进程的线程作为一个ProcessThread对象的集合。 这个类有方法来设置ProcessorAffinity,甚至设置一个首选的处理器 – 不知道这是什么。
免责声明:我曾经遇到类似的问题,我认为CPU(s)利用不足,并研究了很多这些东西; 然而,基于我所读到的所有内容,似乎并不是一个好主意,正如这里发布的评论所certificate的那样。 然而,这仍然是一个有趣的尝试和学习的经验。
你不必担心自己做这个。 我有multithreading.NET应用程序在双四机器上运行,不pipe线程是如何启动的,无论是通过ThreadPool还是手动启动,我都能在所有内核之间看到一个很好的工作分配。
您可以通过在程序中编写例程来完成此任务。
但是你不应该这样做,因为操作系统是pipe理这些东西的最佳人选。 我的意思是用户模式程序不应该尝试去做。
然而,有时候,为了实现负载均衡,甚至可以完成真正的multithreading多核问题(数据竞争/caching一致性),因为不同的线程将在不同的处理器上真正执行。
话虽如此,如果你还想实现,我们可以用下面的方法来做。 我为您提供了(Windows操作系统)的伪代码,但是它们也可以轻松地在Linux上完成。
#define MAX_CORE 256 processor_mask[MAX_CORE] = {0}; core_number = 0; Call GetLogicalProcessorInformation(); // From Here we calculate the core_number and also we populate the process_mask[] array // which would be used later on to set to run different threads on different CORES. for(j = 0; j < THREAD_POOL_SIZE; j++) Call SetThreadAffinityMask(hThread[j],processor_mask[j]); //hThread is the array of handles of thread. //Now if your number of threads are higher than the actual number of cores, // you can use reset the counters(j) once you reach to the "core_number".
在调用上面的例程之后,线程总是以如下方式执行:
Thread1-> Core1 Thread2-> Core2 Thread3-> Core3 Thread4-> Core4 Thread5-> Core5 Thread6-> Core6 Thread7-> Core7 Thread8-> Core8 Thread9-> Core1 Thread10-> Core2 ...............
有关更多信息,请参阅手册/ MSDN以了解有关这些概念的更多信息。
每个线程去的地方通常由操作系统本身来处理…所以在4核心系统上生成4个线程,操作系统将决定每个核心运行哪个核心,每个核心通常是1个线程。
操作系统的工作是在不同内核之间分割线程,当线程使用大量CPU时间时,会自动执行。 别担心。 至于找出您的用户有多less个核心,请尝试C#中的Environment.ProcessorCount
。
你不应该(正如前面所说的)试图自己分配这种东西的原因之一就是你没有足够的信息来做正确的事情,特别是在NUMA的将来等等。
如果你有一个线程读取运行,并有一个核心空闲,内核将运行你的线程,不要担心。
你不能这样做,因为只有操作系统才有权这样做。 如果你决定,那么编写应用程序将会很困难。 因为那你也需要注意处理器间的通信。 关键部分。 对于每个应用程序,您必须创build自己的信号量或互斥锁……操作系统通过自己做出一个通用的解决scheme…….
虽然我同意这里的大部分答案,但我认为值得添加一个新的考虑:Speedstep技术。
当在多核系统上运行CPU密集型单线程作业时,在我的情况下,在Windows Server 2012下运行的是具有6个真实核心(12个HT)的Xeon E5-2430,作业分散在所有12个核心之间,每个核心的8.33%左右,从来没有引起速度增加。 CPU保持在1.2 GHz。
当我将线程亲和力设置为特定内核时,它使用了该内核的〜100%,导致CPU在2.5 GHz时达到最大值,性能提高了一倍以上。
这是我使用的程序,只是循环增加一个variables。 当用-a调用时,它将设置亲和力到核心1.亲和力部分基于这个post 。
using System; using System.Diagnostics; using System.Linq; using System.Runtime.InteropServices; using System.Threading; namespace Esquenta { class Program { private static int numThreads = 1; static bool affinity = false; static void Main(string[] args) { if (args.Contains("-a")) { affinity = true; } if (args.Length < 1 || !int.TryParse(args[0], out numThreads)) { numThreads = 1; } Console.WriteLine("numThreads:" + numThreads); for (int j = 0; j < numThreads; j++) { var param = new ParameterizedThreadStart(EsquentaP); var thread = new Thread(param); thread.Start(j); } } static void EsquentaP(object numero_obj) { int i = 0; DateTime ultimo = DateTime.Now; if(affinity) { Thread.BeginThreadAffinity(); CurrentThread.ProcessorAffinity = new IntPtr(1); } try { while (true) { i++; if (i == int.MaxValue) { i = 0; var lps = int.MaxValue / (DateTime.Now - ultimo).TotalSeconds / 1000000; Console.WriteLine("Thread " + numero_obj + " " + lps.ToString("0.000") + " M loops/s"); ultimo = DateTime.Now; } } } finally { Thread.EndThreadAffinity(); } } [DllImport("kernel32.dll")] public static extern int GetCurrentThreadId(); [DllImport("kernel32.dll")] public static extern int GetCurrentProcessorNumber(); private static ProcessThread CurrentThread { get { int id = GetCurrentThreadId(); return Process.GetCurrentProcess().Threads.Cast<ProcessThread>().Single(x => x.Id == id); } } } }
结果是:
处理器速度,如任务pipe理器所示,类似于CPU-Z报告的内容: