欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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

程序员文章站 2022-05-18 12:03:02
问题 有一批数据,需要对每个元素进行相同的操作。该操作是计算密集型的,需要耗费一定的时间。 解决方案 常见的操作可以粗略分为 计算密集型操作 和 IO密集型操作。计算密集型操作主要是依赖于CPU计算,所以可以最大限度利用多核CPU的并行操作非常适合计算密集型操作。图像操作是比较常见的计算密集型操作, ......

问题

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

解决方案

常见的操作可以粗略分为 计算密集型操作 和 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 方法。