C#中多个线程之间的拆分操作

Posted

技术标签:

【中文标题】C#中多个线程之间的拆分操作【英文标题】:Split operations between multiple threads in C# 【发布时间】:2014-09-14 00:00:17 【问题描述】:

我正在编写具有线程数和操作数作为输入参数执行的基准测试应用程序。 每个基准都创建为具有 Execute(int numberOfRepeats) 方法的单独类。 numberOfRepeats 实际上是每个线程中的重复次数。

我通过以下方式创建基准:

例如,我有 32 个线程和 50 个持久的基准操作。所以每个线程必须执行 50/32 = 1 次操作(实际上是 1.56),这将为所有线程提供 32 次操作的总数。

我使用简单的“new Thread()”构造进行多线程处理,并使用带有 WaitHandle.WaitAll 构造的 AutoResetEvent 来同步执行并测量总时间。

我尝试 Parallel.For 使用 ParallelOptions.MaxDegreeOfParallelism 作为线程数,但它实际上并没有对所有线程运行基准测试。对于 100k 的操作数量,只有 20 个线程 使用了来自线程池的 ParallelOptions.MaxDegreeOfParallelism=128

现在是问题。在我描述的情况下,如何在线程之间拆分操作以执行确切数量的操作?

谢谢!

【问题讨论】:

JITTER 将只使用多个线程, 【参考方案1】:

并行调度程序不会使用那么多线程,因为它足够聪明,知道何时这样做会降低性能。

来自MSDN:

.NET 线程池通过以下方式动态适应不断变化的工作负载 允许更改并行任务的工作线程数 随着时间的推移。在运行时,系统观察是否增加 线程数提高或降低整体吞吐量并调整 相应的工作线程数。

如果您使用这么多线程来执行基准测试,您应该重新考虑您的实现。你会降低你的整体性能,因为线程会在每个周期内与每个线程争斗,而这是你在尝试执行对时间敏感的工作(如基准测试)时最不想做的事情。

【讨论】:

我知道线程数过多不是很好,但它是基准测试的一部分,我需要确切的线程数。它们将在具有大量 RAM 的多核机器上运行。【参考方案2】:

我找到了在线程之间拆分任意数量的操作的简单方法。 此方法将返回 int 数组,其中每个元素表示索引为 i 的线程的操作数。

    private static int[] splitTasksForThreads(int numberOfThreads, int numberOfTasks)
    
        var tasksRepeatArray = new int[numberOfThreads];
        var taskPerThread = numberOfTasks / numberOfThreads;
        var diff = numberOfTasks - (numberOfThreads * taskPerThread);

        if (diff == 0)
        
            for (int i = 0; i < numberOfThreads; i++)
                tasksRepeatArray[i] = taskPerThread;
        
        else if (numberOfThreads > numberOfTasks)
        
            for (int i = 0; i < numberOfTasks; i++)
            
                tasksRepeatArray[i]++;
            
        
        else
        
            for (int i = 0; i < tasksRepeatArray.Length; i++)
                tasksRepeatArray[i] = taskPerThread;
            for (int i = 0; i < diff; i++)
                tasksRepeatArray[i]++;
        

        return tasksRepeatArray;
    

【讨论】:

【参考方案3】:

这是cmd 的函数的一个版本,如果操作超过包括 cmets 在内的线程数,它将减少线程数。我对此不以为然。无法发表评论,因为我的声誉不够高。

internal static IEnumerable<int> GetActionsPerThreads(int numberOfThreads, int numberOfActions)
    
        // We have too many threads for the requested amount of actions
        while (numberOfActions < numberOfThreads)
        
            numberOfThreads--;
        

        int[] actionsPerThread = new int[numberOfThreads];
        // Intentional loss of data
        int perThread = numberOfActions / numberOfThreads;
        int actionRemainder = numberOfActions - (numberOfThreads * perThread);

        // No need to split anything. The threads can perform an equal amount of actions
        if (actionRemainder == 0)
        
            for (int i = 0; i < numberOfThreads; i++)
            
                actionsPerThread[i] = perThread;
            
        
        // We have more actions than we have threads. Time to reduce our thread count to the amount of actions
        else if (numberOfThreads > numberOfActions)
        
            for (int i = 0; i < numberOfActions; i++)
            
                actionsPerThread[i]++;
            
        
        // We have an unequal amount of actions per thread, time to split them
        else
        
            // All threads perform the calculated amount of actions (rounded down)
            for (int i = 0; i < actionsPerThread.Length; i++)
            
                actionsPerThread[i] = perThread;
            

            // Some tasks will have to do more work
            for (int i = 0; i < actionRemainder; i++)
            
                actionsPerThread[i]++;
            
        
        return actionsPerThread;
    

【讨论】:

以上是关于C#中多个线程之间的拆分操作的主要内容,如果未能解决你的问题,请参考以下文章

C#多线程调试

C# 线程安全与 lock锁

C#:在多个进程和/或线程之间共享数据的最佳方式

c#异步编程-线程

C# 在多线程环境中,进行安全遍历操作

C# 线程