如果程序中有大量的计算任务,并且这些任务能分割成几个互相独立的任务块,那就应该使用并行编程。

并行编程用于分解计算密集型的任务片段,并将它们分配给多个线程。这些并行处理方法只适用于计算密集型的任务。

一 数据的并行处理

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

Parallel 类型有 ForEach 方法可以解决上述问题。

下例使用了一批矩阵,对每一个矩阵都进行旋转,Matrix类的Rotate方法是计算密集型的任务。

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();
});
}

更常见的情况是可以取消并行循环,这与结束循环不同。结束(stop)循环是在循环内部进行,

而取消(cancel)循环是在循环外部进行的。例如,点击“取消”按钮可以取消一个 CancellationTokenSource,以取消并行循环,如下:

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

注意,每个并行任务可能都在不同的线程中运行,因此必须保护对共享的状态。

二 并行聚合

使用Parallel,在并行操作结束时,可以根据需要聚合结果,包括累加和、平均值等。

Parallel 类通过局部值(local value)的概念来实现聚合,局部值就是只在并行循环内部存在的变量。

这意味着循环体中的代码可以直接访问值,不需要担心同步问题。

循环中的代码使用 LocalFinally 委托来对每个局部值进行聚合。

需要注意的是,localFinally 委托需要以同步的方式对存放结果的变量进行访问。

下面是一个并行求累加和的例子:

//注意,这不是最高效的实现方式,只是举个例子,说明用锁来保护共享状态。
static int ParallelSum(IEnumerable<int> values)
{
object mutex = new object();
int result = 0;
Parallel.ForEach(
source: values,
localInit: () => 0,
body: (item, state, localValue) => localValue + item,
localFinally: localValue =>
{
lock (mutex)
result += localValue;
}
);
return result;
}

并行 LINQ 对聚合的支持,比 Parallel 类更加易用:

static int ParallelSum(IEnumerable<int> values)
{
return values.AsParallel().Sum();
}

PLINQ 本身支持很多常规操作(例如求累加和)。大多数情况下PLINQ 对聚合的支持更有表现力,代码也更少。

PLINQ也可通过 Aggregate 实现通用的聚合功能:

static int ParallelSum(IEnumerable<int> values)
{
return values.AsParallel().Aggregate(
seed: 0,
func: (sum, item) => sum + item
);
}

三 并行调用

如果需要并行调用一批方法,并且这些方法(大部分)是互相独立的。

Parallel 类有一个简单的成员 Invoke,可用于这种场合。

下面的例子将一个数组分为两半,并且分别独立处理:

static void ProcessArray(double[] array)
{
Parallel.Invoke(
() => ProcessPartialArray(array, 0, array.Length / 2),
() => ProcessPartialArray(array, array.Length / 2, array.Length)
);
}
static void ProcessPartialArray(double[] array, int begin, int end)
{
// 计算密集型的处理过程 ...
}

如果在运行之前都无法确定调用的方法数量,就可以在 Parallel.Invoke 函数中输入一个委托数组,Parallel.Invoke 也支持取消操作:

static void DoAction20Times(Action action, CancellationToken token)
{
Action[] actions = Enumerable.Repeat(action, 20).ToArray();
Parallel.Invoke(new ParallelOptions { CancellationToken = token }, actions);
}

对于简单的并行调用,Parallel.Invoke 是一个非常不错的解决方案。

但在以下两种情况中使用 Parallel.Invoke 并不是很合适:

要对每一个输入的数据调用一个操作(改用Parallel.Foreach),或者每一个操作产生了一些输出(改用并行 LINQ)。

四 并行LINQ

LINQ 可以实现在序列上”拉取“数据的运算。并行LINQ(PLINQ)扩展了 LINQ,以支持并行处理。

PLINQ 非常适用于数据流的操作,一个数据队列作为输入,一个数据队列作为输出。

下面简单的例子将序列中的每个元素都乘以2:

static IEnumerable<int> MultiplyBy2(IEnumerable<int> values)
{
return values.AsParallel().Select(item => item * 2); //实际应用中,计算工作量要大得多
}

按照并行 LINQ 的默认方式,这个例子中输出数据队列的次序是不固定的。

我们可以指明要求保持原来的次序。下面的例子也是并行执行的,但保留了数据的原有次序:

static IEnumerable<int> MultiplyBy2(IEnumerable<int> values)
{
return values.AsParallel().AsOrdered().Select(item => item * 2);
}

Parallel 类可适用于很多场合,但是在做聚合或进行数据序列的转换时,PLINQ 的代码更加简洁。

PLINQ 为各种各样的操作提供了并行的版本,包括过滤(Where)、投影(Select)以及各种聚合运算,

例如 Sum、Average 和更通用的 Aggregate。一般来说,对常规 LINQ 的所有操作都可以通过并行方式对 PLINQ 执行。

以上。

C#并发编程-3 并行编程基础的更多相关文章

  1. C#并行编程-并发集合

    菜鸟学习并行编程,参考<C#并行编程高级教程.PDF>,如有错误,欢迎指正. 目录 C#并行编程-相关概念 C#并行编程-Parallel C#并行编程-Task C#并行编程-并发集合 ...

  2. C#并行编程中的Parallel.Invoke

    一.基础知识 并行编程:并行编程是指软件开发的代码,它能在同一时间执行多个计算任务,提高执行效率和性能一种编程方式,属于多线程编程范畴.所以我们在设计过程中一般会将很多任务划分成若干个互相独立子任务, ...

  3. C#并行编程

    C#并行编程中的Parallel.Invoke 一.基础知识 并行编程:并行编程是指软件开发的代码,它能在同一时间执行多个计算任务,提高执行效率和性能一种编程方式,属于多线程编程范畴.所以我们在设计过 ...

  4. 并行编程(Parallel Framework)

    前言 并行编程:通过编码方式利用多核或多处理器称为并行编程,多线程概念的一个子集. 并行处理:把正在执行的大量的任务分割成小块,分配给多个同时运行的线程.多线程的一种. 并行编程分为如下几个结构: 1 ...

  5. C#并行编程系列-文章导航

    菜鸟初步学习,不对的地方请大神指教,参考<C#并行编程高级教程.pdf> 目录 C#并行编程-相关概念 C#并行编程-Parallel C#并行编程-Task C#并行编程-并发集合 C# ...

  6. C#并行编程-相关概念

    菜鸟初步学习,不对的地方请大神指教,参考<C#并行编程高级教程.pdf> 目录 C#并行编程-相关概念 C#并行编程-Parallel C#并行编程-Task C#并行编程-并发集合 C# ...

  7. C#并行编程-Parallel

    菜鸟学习并行编程,参考<C#并行编程高级教程.PDF>,如有错误,欢迎指正. 目录 C#并行编程-相关概念 C#并行编程-Parallel C#并行编程-Task C#并行编程-并发集合 ...

  8. C#并行编程-Task

    菜鸟学习并行编程,参考<C#并行编程高级教程.PDF>,如有错误,欢迎指正. 目录 C#并行编程-相关概念 C#并行编程-Parallel C#并行编程-Task C#并行编程-并发集合 ...

  9. C#并行编程-线程同步原语

    菜鸟学习并行编程,参考<C#并行编程高级教程.PDF>,如有错误,欢迎指正. 目录 C#并行编程-相关概念 C#并行编程-Parallel C#并行编程-Task C#并行编程-并发集合 ...

随机推荐

  1. DelayQueue达到定时触发效果

    DelayQueue的特点就是插入Queue中的数据可以按照自定义的delay时间进行排序.只有delay时间小于0的元素才能够被取出. 这样子,只要开启一个线程循环从DelayQueue中取值执行, ...

  2. 在CDH webUI中部署HDFS HA

    一.点击hdfs按钮进入hdfs配置界面 二.开始部署hdfs ha 三.分配角色 设置存储路径,这个可以自定义,我还在学习阶段我就默认了,之前改过,没起来,默认就好了:

  3. 2022-07-29 java之异常

    目录 一.异常 1. 异常的概念 2. 异常体系 3.图解异常处理的流程 4.异常处理 1.抛出异常throw 2.捕获异常try{}catch{} 3 声明异常throws 4. finally代码 ...

  4. Javaweb03-servlet&filter

    1.什么是servlet 第一个servlet应用程序,实现servlet接口 定义:凡是实现了servlet api接口(或者继承servlet api接口的实现类)的类就是servlet应用程序: ...

  5. SQL注入 基础学习

    SQL注入学习笔记 注入原理 SQL注入攻击指的是通过构建特殊的输入作为参数传入Web应用程序,而这些输入大都是SQL语法里的一些组合,通过执行SQL语句进而执行攻击者所要的操作,其主要原因是程序没有 ...

  6. Python爬虫:为什么你爬取不到网页数据

    前言: 之前小编写了一篇关于爬虫为什么爬取不到数据文章(文章链接为:Python爬虫经常爬不到数据,或许你可以看一下小编的这篇文章), 但是当时小编也是胡乱编写的,其实里面有很多问题的,现在小编重新发 ...

  7. mybatis 07: sql标签中 "#{}" 和 "${}" 的作用和比较

    "#{}"占位符 作用 传参大部分使用"#{}",在数据库底层使用的是:PreparedStatement预编译处理对象 数据库底层被解析为"?&qu ...

  8. redis-hash命令

    一.HDEL key field [field ...] 从 key 指定的哈希集中移除指定的域.在哈希集中不存在的域将被忽略. 如果 key 指定的哈希集不存在,它将被认为是一个空的哈希集,该命令将 ...

  9. 未来的可再生能源电网!FREEDM 论文阅读

    全文主旨[省时间快读] 背景: 论文标题:The Future Renewable Electric Energy Delivery and Management (FREEDM) System: T ...

  10. [CISCN2019 华北赛区 Day1 Web2]ikun-1

    考点:JWT身份伪造.python pickle反序列化.逻辑漏洞 1.打开之后首页界面直接看到了提示信息,信息如下: 2.那就随便注册一个账号进行登录,然后购买lv6,但是未发现lv6,那就查看下一 ...