Loading

《C#并发编程经典实例》学习笔记—3.1 数据的并行处理

问题

有一批数据,需要对每个元素进行相同的操作。该操作是计算密集型的,需要耗费一定的时间。

解决方案

常见的操作可以粗略分为 计算密集型操作 和 IO密集型操作。计算密集型操作主要是依赖于CPU计算,所以可以最大限度利用多核CPU的并行操作非常适合计算密集型操作。图像操作是比较常见的计算密集型操作,图像操作一般是借助矩阵存储图像数据,该书作者就举了矩阵旋转为例。
思路是借助Parallel.ForEach实现并行操作。
伪代码如下

void RotateMatrices(IEnumerable<Matrix> matrices, float degrees)
{
    Parallel.ForEach(matrices, matrix => matrix.Rotate(degrees));
}

当前循环处理无效值时,可能需要停止该循环,实现如下

void InvertMatrices(IEnumerable<Matrix> matrices)
{
    Parallel.ForEach(matrices, (matrix, state) =>
    {
        if (!matrix.IsInvertible)
            state.Stop();
        else
            matrix.Invert();
    });
}

而如果是想要取消整个并行循环,则需要借助CancellationToken,即可以有一个取消按钮,点击取消按钮,应取消循环操作。

void RotateMatrices(IEnumerable<Matrix> matrices, float degrees,
CancellationToken token)
{
    Parallel.ForEach(matrices,
    new ParallelOptions { CancellationToken = token },
    matrix => matrix.Rotate(degrees));
}

另:当一个全局变量在并行循环内部被操作时,则需要考虑多进程共享状态。因为并行循环的每个循环很可能在不同的线程中运行。

// 注意,这不是最高效的实现方式。
// 只是举个例子,说明用锁来保护共享状态。
int InvertMatrices(IEnumerable<Matrix> matrices)
{
    object mutex = new object();
    int nonInvertibleCount = 0;
    Parallel.ForEach(matrices, matrix =>
    {
        if (matrix.IsInvertible)
        {
            matrix.Invert();
        }
        else
        {
            lock (mutex)
            {
                ++nonInvertibleCount;
            }
        }
    });
    return nonInvertibleCount;
}

《C#并发编程经典实例》学习笔记-第一章并发编程概述 - repeatedly - 博客园 ,我提到了并行操作的两种方式,一种是Parallel,另一种是PLINQ(Parallel LINQ)。所以上述操作也可以使用PLINQ实现。

两者是有区别的,区别如下:

Parallel 类和 PLINQ 之间有一个区别:PLINQ 假设可以使用计算机内所有的CPU 核,而 Parallel 类则会根据 CPU 状态的变化动态地调整。

相信对C#语法比较熟悉的应该能看出来,Parallel.ForEach是并行foreach循环,那么并行for循环对应的方法是什么呢?是Parallel.For 方法。

posted @ 2019-10-23 17:13  repeatedly  阅读(758)  评论(2编辑  收藏  举报