Tom*_*idd 58 .net c# windows multithreading
假设我在C#中创建了一个计算成本高的程序,比如将WAV文件列表编码为MP3.通常我会一次编码一个文件,但是假设我想让程序计算出我拥有多少CPU核心并在每个核心上启动编码线程.因此,当我在四核CPU上运行程序时,程序会发现它是一个四核CPU,可以看出有四个内核可以使用,然后生成四个用于编码的线程,每个线程都独立运行中央处理器.我该怎么做?
如果核心分布在多个物理CPU上,这会有什么不同吗?如果我有一台带有两个四核CPU的机器,是否有任何特殊注意事项,或者两个芯片中的八个内核在Windows中是否相同?
Joe*_*son 16
它不一定像使用线程池那么简单.
默认情况下,线程池为每个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);
Run Code Online (Sandbox Code Playgroud)
不幸的是,它并不像限制CPU的数量那么简单.您还必须考虑硬盘控制器和磁盘的性能.
您可以真正找到最佳线程数的唯一方法是尝试错误.当您使用硬盘,Web服务等时尤其如此.对于硬盘,最好不要在四处理器CPU上使用所有四个处理器.另一方面,对于某些Web服务,您最好每个CPU发出10个甚至100个请求.
在托管线程的情况下,执行此操作的复杂程度大于本机线程的复杂程度.这是因为CLR线程不直接绑定到本机OS线程.换句话说,CLR可以在其认为合适时将托管线程从本机线程切换到本机线程.提供函数Thread.BeginThreadAffinity以将托管线程与本机OS线程锁定在一起.此时,您可以尝试使用本机API来提供底层本机线程处理器关联.正如大家在这里所说,这不是一个好主意.事实上,有文档表明如果线程仅限于单个处理器或核心,则线程可以获得更少的处理时间.
您还可以浏览System.Diagnostics.Process类.在那里,您可以找到一个函数来枚举进程'线程作为ProcessThread对象的集合.这个类有设置ProcessorAffinity甚至设置首选处理器的方法 - 不知道那是什么.
免责声明:我遇到了一个类似的问题,我认为CPU的使用不足并研究了很多这样的东西; 然而,根据我读到的所有内容,似乎这不是一个好主意,这里也发表了评论.然而,它仍然是有趣的和实验的学习经验.
虽然我同意这里的大多数答案,但我认为增加一个新的考虑是值得的: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的亲和性.亲和力部分基于此帖子.
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);
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
结果如下:

处理器速度,如任务管理器所示,类似于CPU-Z报告的内容:

您绝对可以通过在程序内部编写例程来完成此操作。
但是,您不应该尝试这样做,因为操作系统是管理这些内容的最佳人选。我的意思是用户模式程序不应尝试这样做。
但是,有时可以做到(对于真正的高级用户),以实现负载平衡,甚至找出真正的多线程多核问题(数据竞速/缓存一致性...),因为不同的线程将真正在不同的处理器上执行。
话虽如此,如果您仍然想实现目标,我们可以通过以下方式实现。我正在为您提供(Windows OS)的伪代码,但是它们也可以在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".
Run Code Online (Sandbox Code Playgroud)
在调用上述例程之后,线程将始终以以下方式执行:
Thread1-> Core1
Thread2-> Core2
Thread3-> Core3
Thread4-> Core4
Thread5-> Core5
Thread6-> Core6
Thread7-> Core7
Thread8-> Core8
Thread9-> Core1
Thread10-> Core2
...............
Run Code Online (Sandbox Code Playgroud)
有关更多信息,请参阅手册/ MSDN以了解有关这些概念的更多信息。
| 归档时间: |
|
| 查看次数: |
51627 次 |
| 最近记录: |