2008-08-28 43 views
50

比方说,我有一个C#中的程序,它做了一些昂贵的计算,比如将一个WAV文件列表编码为MP3。通常我会一次对一个文件进行编码,但是假设我想让程序找出我有多少个CPU核心,并在每个核心上启动一个编码线程。所以,当我在四核CPU上运行程序时,程序会发现它是一个四核CPU,它指出有四个内核可以工作,然后为编码生成四个线程,每个线程都独立运行中央处理器。我将如何做到这一点?如何在不同的CPU内核上生成线程?

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

回答

51

不要这么做。

改为使用Thread Pool。线程池是您可以查询新线程的框架机制(实际上是一个类)。

当你问了一个新的线程,它要么给你一个新的或排队的工作,直到线程得到释放。通过这种方式,框架负责决定是否应该创建更多线程,具体取决于当前CPU的数量。

编辑:另外,如前所述,OS负责在不同的CPU之间分配线程。

+1

如果你的避风港”没有.NET? – Crashworks 2009-02-20 00:59:57

+52

这是一个.NET相关的问题。为什么你没有.NET? – 2009-03-03 20:49:57

1

其中每个线程去一般是由操作系统本身来处理......所以产生4核系统上4个线程和操作系统将决定哪些内核运行的每个上,这通常是1个线程每个核心。

1

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

2

你不应该担心自己这样做。我有多线程的.NET应用程序在双四机器上运行,无论线程是如何启动的,无论是通过ThreadPool还是手动启动,我都可以在所有内核之间看到平均分配的工作。

1

一个你不应该(正如人们所说的)尝试分配这种东西你自己的原因,是你根本没有足够的信息来操作正确,特别是进入未来NUMA,等等。

如果你有一个线程read-to-run,并有一个核心空闲,内核运行你的线程,不用担心。

8

在托管线程的情况下,这样做的复杂度比本地线程的复杂程度要大。这是因为CLR线程不直接绑定到本地操作系统线程。换句话说,CLR可以按照它认为合适的方式将线程从本地线程切换到本地线程。提供函数Thread.BeginThreadAffinity是为了与本地OS线程锁定一个托管线程。此时,您可以尝试使用本机API来提供底层本机线程处理器关联。正如大家在这里所暗示的,这不是一个好主意。事实上有documentation暗示如果线程限于单个处理器或核心,线程可以接收较少的处理时间。

您还可以探索System.Diagnostics.Process课程。在那里你可以找到一个函数来枚举一个进程的线程作为一个对象的集合。这个类有设置ProcessorAffinity的方法,甚至设置首选的处理器 - 不确定那是什么。

声明:我所经历的,我认为CPU(S)是利用不足,并研究了很多这方面的东西了类似的问题;然而,根据我读到的所有内容,似乎并不是一个好主意,正如这里发布的评论所证明的那样。然而,它仍然是有趣的,并且是一次实验的学习体验。

17

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

默认情况下,线程池为每个CPU分配多个线程。由于涉及你所做的工作的每个线程都有成本(任务切换开销,使用CPU非常有限的L1,L2和L3缓存等等),因此使用的最佳线程数为< =可用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的数量一样简单。您还必须考虑硬盘控制器和磁盘的性能。

确实找到最佳线程数的唯一方法是试用一个错误。当您使用硬盘,Web服务等时尤其如此。使用硬盘时,最好不要在四核处理器CPU上使用全部四个处理器。另一方面,对于某些Web服务,每个CPU可能会更好地完成10个甚至100个请求。

1

你不能这样做,因为只有操作系统才有权这样做。如果你决定......那么编写应用程序将很困难。因为那样你也需要注意处理器间的通信。关键部分。对于每个应用程序,你必须创建你自己的信号或互斥......操作系统通过自己做出一个共同的解决方案.......

2

你可以通过编写例程在你的程序里面。

然而,你不应该这样做,因为操作系统是管理这些东西的最佳人选。我的意思是用户模式程序不应该尝试去做。但是,有时候,它可以完成(对于真正的高级用户)来实现负载均衡,甚至可以找出真正的多线程多核问题(数据竞争/缓存一致性...),因为不同的线程将是真正的在不同处理器上执行。

话虽如此,如果您仍想实现,我们可以通过以下方式完成。我为您提供了(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更多地了解这些概念。

1

尽管我同意这里的大多数答案,但我认为值得添加一个新的考虑:Speedstep技术。

当在多核系统上运行CPU密集型单线程作业时,在我的情况下,Windows Server 2012下包含6个真正核心(12个HT)的Xeon E5-2430,作业在所有12个内核,每个内核使用率约为8.33%,从未引发速度提升。 CPU保持在1.2 GHz。

当我将线程关联设置为特定内核时,它使用了该内核的〜100%,导致CPU在2.5 GHz时达到最大值,性能提高了一倍以上。

这是我使用的程序,它只是循环增加一个变量。当用-a调用时,它将设置亲和性为核心1.亲和性部分基于this 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); 
      } 
     } 
    } 
} 

而且结果:

results

处理器速度,如通过任务管理器,类似于CPU-Z报告:

enter image description here

相关问题