当前位置:  开发笔记 > 编程语言 > 正文

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

如何解决《如何在不同的CPU内核上生成线程?》经验,为你挑选了5个好方法。

假设我在C#中创建了一个计算成本高的程序,比如将WAV文件列表编码为MP3.通常我会一次编码一个文件,但是假设我想让程序计算出我拥有多少CPU核心并在每个核心上启动编码线程.因此,当我在四核CPU上运行程序时,程序会发现它是一个四核CPU,可以看出有四个内核可以使用,然后生成四个用于编码的线程,每个线程都独立运行中央处理器.我该怎么做?

如果核心分布在多个物理CPU上,这会有什么不同吗?如果我有一台带有两个四核CPU的机器,是否有任何特殊注意事项,或者两个芯片中的八个内核在Windows中是否相同?



1> Jorge Córdob..:

不要那么麻烦.

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

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

编辑:此外,正如已经提到的,操作系统负责在不同的CPU之间分配线程.


这是一个与.NET相关的问题.你为什么不用.NET?

2> Joe Erickson..:

它不一定像使用线程池那么简单.

默认情况下,线程池为每个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个请求.


使用的最佳线程数比CPU数量多一点.你的相反论点是错误的.如果由于线程无法再向前进行而发生任务切换,则无论您创建了多少线程,都将执行该任务切换.完全使用时间片的任务切换可以忽略不计,因为操作系统会仔细选择时间片来确保这一点.

3> Peter Meyer..:

在托管线程的情况下,执行此操作的复杂程度大于本机线程的复杂程度.这是因为CLR线程不直接绑定到本机OS线程.换句话说,CLR可以在其认为合适时将托管线程从本机线程切换到本机线程.提供函数Thread.BeginThreadAffinity以将托管线程与本机OS线程锁定在一起.此时,您可以尝试使用本机API来提供底层本机线程处理器关联.正如大家在这里所说,这不是一个好主意.事实上,有文档表明如果线程仅限于单个处理器或核心,则线程可以获得更少的处理时间.

您还可以浏览System.Diagnostics.Process类.在那里,您可以找到一个函数来枚举进程'线程作为ProcessThread对象的集合.这个类有设置ProcessorAffinity甚至设置首选处理器的方法 - 不知道那是什么.

免责声明:我遇到了一个类似的问题,我认为CPU的使用不足并研究了很多这样的东西; 然而,根据我读到的所有内容,似乎这不是一个好主意,这里也发表了评论.然而,它仍然是有趣的和实验的学习经验.



4> AlexDev..:

虽然我同意这里的大多数答案,但我认为增加一个新的考虑是值得的: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().Single(x => x.Id == id);
            }
        }
    }
}

结果如下:

结果

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

在此输入图像描述



5> Mantosh Kuma..:

您绝对可以通过在程序内部编写例程来完成此操作。

但是,您不应该尝试这样做,因为操作系统是管理这些内容的最佳人选。我的意思是用户模式程序不应尝试这样做。

但是,有时可以做到(对于真正的高级用户),以实现负载平衡,甚至找出真正的多线程多核问题(数据竞速/缓存一致性...),因为不同的线程将真正在不同的处理器上执行。

话虽如此,如果您仍然想实现目标,我们可以通过以下方式实现。我正在为您提供(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".

在调用上述例程之后,线程将始终以以下方式执行:

Thread1-> Core1
Thread2-> Core2
Thread3-> Core3
Thread4-> Core4
Thread5-> Core5
Thread6-> Core6
Thread7-> Core7
Thread8-> Core8

Thread9-> Core1
Thread10-> Core2
...............

有关更多信息,请参阅手册/ MSDN以了解有关这些概念的更多信息。

推荐阅读
jerry613
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有