c# - 有没有办法改变 Parallel.Foreach 交织其操作的数组的方式?

标签 c# multithreading linq fractals win2d

我正在使用 Parallel.Foreach 对大型数组进行操作,其中每个数组元素对应于位图中的一行。但是,Parallel.Foreach 似乎分配固定数量的线程,比如 6 个线程,并将数组的 1/6 分配给第一个线程,将数组的 1/6 分配给下一个线程命令。示例:

[0]=>thread1, [1]=>thread1, [2]=>thread1, [3]=>thread1

[4]=>thread2, [5]=>thread2, [6]=>thread2, [7]=>thread2,

.. and so forth

enter image description here

粗略地说,我想要的是一个交错模式,它在数组索引的每个增量的线程之间交替......例如:

[0]=>thread1 [1]=>thread2 [2]=>thread3 [4]=>thread4, [5]=>thread1, [6]=>thread2, etc...

有没有办法改变 Parallel.Foreach 分配给每个主动执行的并行线程的交错模式?

我正在尝试使用 gif 交错模式渲染图像,而不是让位图线在图像上以 6 条不同的线顺序呈现,向下填充...

using System.Threading.Tasks;
using System.Threading;
using System.Collections.Concurrent;

//…

void button_click() {
    Task.Run(() =>{
        start_task();
    });
}

int ds_height=600;
public bool start_task()
{
    var cpu75 = Convert.ToInt32(Math.Ceiling((
           Environment.ProcessorCount * 0.75) * 1.0));
    //MandelInit();

    int[] ytmp;

    int version = 2;
    if (version == 1) {
        ytmp = MandelYLace();
    }
    else {
        int[] ytmp = new int[ds_height];
        for(int i=0; i < ds_height; i++)
        {
            ytmp[i] = i;
        }
        Parallel.ForEach(
            ytmp, //ylace,
            new ParallelOptions { MaxDegreeOfParallelism = cpu75 },
            yy => {
                //ybuff[yy] = MandelLine(yy);
                //ydone.Enqueue(yy);
            }
        );
        stop = true;
        return true;
}

// Interlace Y-Lines using GIF Interlaced method
int[] MandelYLace()
{
    var ylace = new int[ds_height];
    for (int y = 0, yg=0, yy=0; y < ds_height; y++)
    {
        ylace[y] = yy;

        if (yg == 0 || yg == 1)
            yy += 8;
        else if (yg == 2)
            yy += 4;
        else if (yg == 3)
            yy += 2;

        if (yy >= ds_height)
        {
            yg = (yg + 1) % 4;
            if (yg == 1) yy = 4;
            else if (yg == 2) yy = 2;
            else if (yg == 3) yy = 1; 
            else if (yg == 0) yy = 0; 
        }
    }

    return ylace;
}

最佳答案

在这里您可以看到隔行扫描第一遍的快照......一些任务滞后......因此缺少行......它们最终出现:

代码解:enter image description here

int ds_height=600;

public bool start_task()
{
    var cpu75 = Convert.ToInt32(Math.Ceiling((
           Environment.ProcessorCount * 0.75) * 1.0));

    var partitioner = new InterlacePartitioner(ds_height);

    Parallel.ForEach(
            partitioner,
            new ParallelOptions { MaxDegreeOfParallelism = cpu75 },
            yy => {
                //ybuff[yy] = MandelLine(yy);
                //ydone.Enqueue(yy);
            }
    );
    stop = true;
    return true;
}

使用共享 IEnumerator 实现 gif 样式行交错的自定义 PLINQ 分区器:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Collections.Concurrent;
using System.Collections;

namespace NetMandelbrot
{
    // This is both the Interlaced Line IEnumerator and IEnumerable 
public class ImageInterlace : 
     IEnumerable<int>, IEnumerable, IEnumerator<int>, IDisposable
{
    int[] Lines;
    int Pos;

    public ImageInterlace(int ImageHeight)
    {
        Pos = -1;
        Lines = GetLines(ImageHeight);
    }

    // Interlace Y-Lines using GIF Interlaced method
    int[] GetLines(int ImageHeight)
    {
        var ylace = new int[ImageHeight];
        for (int y = 0, yg = 0, yy = 0; y < ImageHeight; y++)
        {
            ylace[y] = yy;

            if (yg == 0 || yg == 1)
                yy += 8;
            else if (yg == 2)
                yy += 4;
            else if (yg == 3)
                yy += 2;

            if (yy >= ImageHeight)
            {
                yg = (yg + 1) % 4;
                if (yg == 1) yy = 4;
                else if (yg == 2) yy = 2;
                else if (yg == 3) yy = 1;
                else if (yg == 0) yy = 0;
            }
        }
        return ylace;
    }

    #region Implementation of IDisposable

    public void Dispose()
    {
    }

    #endregion

    #region Implementation of IEnumerable<int>
    public IEnumerator<int> GetEnumerator()
    {
        return this;
    }
    #endregion

    // Legacy C#
    #region Implementation of IEnumerable
    IEnumerator IEnumerable.GetEnumerator()
    {
        return this;
    }
    #endregion

    #region Implementation of IEnumerator<int>
    public bool MoveNext()
    {
        bool done;
        lock (Lines)
        {
            if (Pos < Lines.Length)
            {
                Pos++;
            }
            done = (Pos < Lines.Length);
        }
        return done;
    }

    public void Reset()
    {
        lock (Lines)
        {
            Pos = -1;
        }
    }

    public int Current
    {
        get
        {
            int nextline;
            lock (Lines)
            {
                if (Pos >= Lines.Length)
                {
                    nextline = -1;
                }
                else
                {
                    nextline = Lines[Pos];
                }
            }
            return nextline;
        }
    }

    // C# Legeacy
    object IEnumerator.Current
    {
        get { return Current; }
    }

    #endregion
}

public class InterlacePartitioner : Partitioner<int>
{
    int ImageHeight = 0;
    ImageInterlace imageinterlace;

    public InterlacePartitioner(int imageHeight)
    {
        ImageHeight = imageHeight;
        imageinterlace = new ImageInterlace(ImageHeight);
    }

    public override IList<IEnumerator<int>> GetPartitions(int partitionCount)
    {
        int i = 0;

        List<List<int>> partz = new List<List<int>>();

        foreach (var yline in imageinterlace)
        {
            partz[i % partitionCount].Add(yline);
            i++;
        }
        return (IList<IEnumerator<int>>)partz;
    }

    public override IEnumerable<int> GetDynamicPartitions()
    {
        return imageinterlace;
    }

    // Not consumable from Parallel.ForEach.
    public override bool SupportsDynamicPartitions
    {
        get
        {
            return true;
        }
    }
} //end of class

}

关于c# - 有没有办法改变 Parallel.Foreach 交织其操作的数组的方式?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52457816/

相关文章:

c# - 显示来自动态加载的 DLL 的表单

c# linq 相当复杂的排序

c# - 使用和或运算符的 Mongodb c# 过滤器

asp.net - Node.js 的事件驱动有什么不同?我们不能在 ASP.Net 的 HttpAsyncHandler 中做到这一点吗?

c++ - std::thread 可以用在构造函数初始化列表中吗?

php - LINQ 与 PDO 类似吗?

c# - 将许多 TimeSpans 减少为更少的平均 TimeSpans 的干净方法?

c# - VB5 dll,如何从 C# ( .NET 4.5 ) 调用函数

c# - Windows 10 通用应用程序 - 标题栏中的按钮不起作用

java - 线程上下文类加载器和普通类加载器的区别