首页 > 解决方案 > 高效地“剪切”多个线程的作业列表

问题描述

我有一个List包含几个Mesh-Objects 的。这是这样一个网格类的样子:

public class Mesh
{
    public int GridWidth { get; }
    public int GridHeight { get; }
    public List<File> Files { get; }
    /* ... */
}

List网格对象内的文件包含-Objects File,这些对象主要由一个带有文件系统路径的字符串和一个二维数组组成,该数组将在文件被解析后保存文件的内容。

public class File
{
    public string Path { get; }
    public double[][] Matrix { get; set; }
    /* ... */
}

多线程和解析工作正常。我决定启动与我的 CPU 具有单核一样多的线程。就我而言:4。

在 Linq 的帮助下,我首先将所有文件对象集中在一个自己的列表中:

List<File> allFiles = meshes.SelectMany(mesh => mesh.Files).ToList();

之后,每个线程从该列表中获取 1/4 的对象并开始解析文件。

这是我的问题:相同大小的文件位于同一个网格内(GridWidth* GridHeight= 解析的矩阵单元数)。在这一点上,可能会偶然发生一个线程只获取大尺寸的文件,而另一个线程只获取小尺寸的文件。在这种情况下,一个线程会比其他线程更早完成——我不希望这样,因为那样效率低下。

所以我有想法首先根据网格的大小对网格列表进行排序,然后将它们的文件定向添加到剪切排序方法(或蛇形排序)到每个线程的新列表中。以下算法有效。但我认为他们可能有一些改进的余地。

这些是我的问题:这个算法是否已经足够高效或者是否存在更好的方法来为每个线程提供文件列表?如果没有更好的方法,我会对“更智能”的编码方式感兴趣(for 循环的所有 if/else 和模运算似乎有点复杂)。

int cores = 4;
List<File>[] filesOfThreads = new List<Slice>[cores];

List<File> allFilesDesc = meshes.OrderByDescending(mesh => mesh.GridWidth * mesh.GridHeight).SelectMany(mesh => mesh.Files).ToList();

int threadIndex = 0;
/*
 * Inside this for-loop the threadIndex changes
 * with each cycle in this way (in case of 4 cores):
 * 0->1->2->3->3->2->1->0->0->1->2->3->3->2 ...
 * With each cycle a file of the current position of 
 * allFilesDesc[i] is added to the list of
 * filesOfThreads[threadIndex]. In this "shear" sort
 * way every thread should get approximately the same
 * number of big and small files.
 */
for (int i = 0; i < allFilesDesc.Count; i++)
{
    if (i < cores)
    {
        filesOfThreads[threadIndex] = new List<File>();
    }
    filesOfThreads[threadIndex].Add(allFilesDesc[i]);
    if (i < cores - 1)
    {
        threadIndex++;
    }
    else if ((i + 1) % cores != 0)
    {
        threadIndex += ((i + 1) / cores) % 2 == 0 ? 1 : -1;
    }
}

foreach (var files in filesOfThreads)
{
    Thread thread = new Thread(() => ComputeFiles(files));
    thread.Start();
}

标签: c#multithreadinglistlinqsorting

解决方案


我的建议

/// <summary>
/// Helper methods for the lists.
/// </summary>
public static class ListExtensions
{
public static List<List<T>> ChunkBy<T>(this List<T> source, int chunkSize) 
{
    return source
        .Select((x, i) => new { Index = i, Value = x })
        .GroupBy(x => x.Index / chunkSize)
        .Select(x => x.Select(v => v.Value).ToList())
        .ToList();
}
}

例如,如果您将 18 个项目的列表按每个块 5 个项目进行排序,它会为您提供 4 个子列表的列表,其中包含以下项目:5-5-5-3。


推荐阅读