并行开发的概念

并行开发要做的事情就是将任务分摊给硬件线程去并行执行来达到负载和加速,传统的代码都是串行的,就一个主线程,当我们为了实现加速而开了很多工作线程,这些工作线程就是软件线程

Parallel的使用

Parallel类是对线程的抽象,位于System.Threading.Tasks名称空间下,提供了任务和数据并行性.在Parallel下有三个常用的方法Invoke,For和ForEach,其中Parallel.Invoke用于任务并行性,Parallel.ForEach/Parallel.For用于数据并行性

Parallel.Invoke

如果多个任务应并行运行,就可以使用Parallel.Invoke()方法最简单,最简洁的将串行的代码并行化

简单应用

class ThreadTest
{
static void Main(string[] args)
{
var watch = Stopwatch.StartNew();
watch.Start();
Run1();
Run2();
Run3();
watch.Stop();
Console.WriteLine("串行开发,总耗时{0}", watch.ElapsedMilliseconds); watch.Restart(); Parallel.Invoke(Run1, Run2, Run3);
watch.Stop();
Console.WriteLine("并行开发,总耗时{0}", watch.ElapsedMilliseconds);
Console.ReadKey();
}
static void Run1()
{
Console.WriteLine("Run1,我需要1s");
Thread.Sleep();
}
static void Run2()
{
Console.WriteLine("Run2,我需要3s");
Thread.Sleep(3);
}
static void Run3()
{
Console.WriteLine("Run3,我需要4s");
Thread.Sleep(4);
}
}

主程序启动时,先顺序调用Run1(),Run()2,Run3()方法,这是串行的,而后使用Parallel.Invoke()将三个方法并行调用,可见耗时是有明显下降的

执行顺序

static void Main(string[] args)
{
Console.WriteLine("主线程启动,线程ID:{0}", Thread.CurrentThread.ManagedThreadId);
Parallel.Invoke(() => Run1("task1"), () => Run2("task2"), () => Run3("task3"));
Console.WriteLine("主线程结束,线程ID:{0}", Thread.CurrentThread.ManagedThreadId);
Console.ReadKey();
}
static void Run1(string taskName)
{
Console.WriteLine("任务名:{0}线程ID:{1}", taskName, Thread.CurrentThread.ManagedThreadId);
for (int i = ; i < ; i++)
{
Console.WriteLine("a");
}
}
static void Run2(string taskName)
{
Console.WriteLine("任务名:{0}线程ID:{1}", taskName, Thread.CurrentThread.ManagedThreadId);
for (int i = ; i < ; i++)
{
Console.WriteLine("b");
}
}
static void Run3(string taskName)
{
Console.WriteLine("任务名:{0}线程ID:{1}", taskName, Thread.CurrentThread.ManagedThreadId);
for (int i = ; i < ; i++)
{
Console.WriteLine("c");
}
}

结果可知:

1、没有固定顺序,每个Task可能是不同的线程去执行,也可能是相同的

2、主线程必须等Invoke中的所有方法执行完成后返回才继续向下执行,以后设计并行的时候,要考虑每个Task任务尽可能差不多,如果相差很大,比如一个时间非常长,其他都比较短,这样一个线程可能会影响整个任务的性能。这点非常重要(就是说Invoke会阻塞主线程)

Parallel.For

Parallel.For是 for 的多线程实现,串行代码中也有一个for,但是那个for并没有用到多核,而Paraller.For它会在底层根据硬件线程的运行状况来充分的使用所有的可利用的硬件线程

static void Main(string[] args)
{
for (int i = ; i < ; i++)
{
ConcurrentBag<int> bag = new ConcurrentBag<int>();
var watch = Stopwatch.StartNew();
watch.Start(); for (int j = ; j < ; j++)
{
bag.Add(i);
}
watch.Stop();
Console.WriteLine("串行添加,总数20000000,耗时{0}", watch.ElapsedMilliseconds);
GC.Collect();
watch.Restart();
Parallel.For(, , j =>
{
bag.Add(j);
});
watch.Stop();
Console.WriteLine("并行添加,总数20000000,耗时{0}", watch.ElapsedMilliseconds);
Console.WriteLine("***********************************");
GC.Collect();
}
Console.ReadKey();
}

向一个线程安全的集合插入数据,使用串行的for耗时与使用并行的Parallel.For差异

Parallel.ForEach

Parallel.ForEach 是 foreach 的多线程实现,他们都能对 IEnumerable<T> 类型对象进行遍历,Parallel.ForEach 的特殊之处在于它使用多线程来执行循环体内的代码段

static void Main(string[] args)
{
ConcurrentBag<int> bag = new ConcurrentBag<int>();
Parallel.For(, , j =>
{
bag.Add(j);
});
Console.WriteLine("集合总数:{0}", bag.Count);
Parallel.ForEach(bag, item =>
{
Console.WriteLine(item);
});
Console.ReadKey();
}

Parallel.ForEach的分区

TODO

中断

Parallel.For:添加ParallelLoopState参数,该实例提供了Break和Stop方法来帮助实现

static void Main(string[] args)
{
ConcurrentBag<int> bag = new ConcurrentBag<int>();
var watch = Stopwatch.StartNew();
watch.Start();
Parallel.For(, , (j, state) =>
{
if (bag.Count == )
{
state.Break();
       //return是必须的,否则依旧会继续执行
return;
}
bag.Add(j);
});
watch.Stop();
Console.WriteLine("集合元素个数{0}", bag.Count);
Console.ReadKey();
}

ParallelLoopState.Break():在完成当前的这轮工作之后,不再执行后继的工作,但在当前这轮工作开始之前“已经在执行”的工作,则必须完成。但并不能执行完所有的循环。
ParallelLoopState.Stop:不但不会再创建新的线程执行并行循环,而且当前“已经在执行”的工作也应该被中止。

注意:Stop仅仅通知其他迭代尽快结束,而Break不仅通知其他迭代尽快结束,同时还要保证退出之前要完成LowestBreakIteration之前的迭代。 例如,对于从 0 到 1000 并行迭代的 for 循环,如果从第 100 此迭代开始调用 Break,则低于 100 的所有迭代仍会运行,从 101 到 1000 的迭代则不必要。而调用Stop方法不保证低于 100 的所有迭代都会运行。

这里发现一个问题

ParallelLoopState.Break()

ConcurrentBag<int> bag = new ConcurrentBag<int>();
for (int j = ; j < ; j++)
{
bag = new ConcurrentBag<int>();
Parallel.For(, , (i, state) =>
{
if (bag.Count == )
{
state.Break();
return;//return是必须的,否则依旧会继续执行
}
else
{
bag.Add(i);
}
});
Console.WriteLine("一共添加2000个元素,集合元素实际个数为:{0}", bag.Count);
Console.WriteLine("*************************************************");
}

ParallelLoopState.Stop()

ConcurrentBag<int> bag = new ConcurrentBag<int>();
for (int j = ; j < ; j++)
{
bag = new ConcurrentBag<int>();
Parallel.For(, , (i, state) =>
{
if (bag.Count == )
{
state.Stop();
return;//return是必须的,否则依旧会继续执行
}
else
{
bag.Add(i);
}
});
Console.WriteLine("一共添加2000个元素,集合元素实际个数为:{0}", bag.Count);
Console.WriteLine("*************************************************");
}

TODO:两种方法都无法准确的在元素添加到1000时结束循环,这里需要后续好好查资料看看.

异常处理

任务是并行计算的,处理过程中可能会产生n多的异常

Exception

Exception是可以捕获到两个异常的,断点可见,但是遍历找不到InnerExceptions属性?

static void Main(string[] args)
{
Console.WriteLine("主线程启动,线程ID:{0}", Thread.CurrentThread.ManagedThreadId);
try
{
Parallel.Invoke(() => Run1("task1"), () => Run2("task2"), () => Run3("task3"));
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
Console.WriteLine("主线程结束,线程ID:{0}", Thread.CurrentThread.ManagedThreadId);
Console.ReadKey();
}
static void Run1(string taskName)
{
Console.WriteLine("任务名:{0}线程ID:{1}", taskName, Thread.CurrentThread.ManagedThreadId);
throw new Exception("Run1出现异常");
}
static void Run2(string taskName)
{
Console.WriteLine("任务名:{0}线程ID:{1}", taskName, Thread.CurrentThread.ManagedThreadId);
for (int i = ; i < ; i++)
{
Console.WriteLine("b");
}
}
static void Run3(string taskName)
{
Console.WriteLine("任务名:{0}线程ID:{1}", taskName, Thread.CurrentThread.ManagedThreadId);
throw new Exception("Run3出现异常");
}

AggregateException

static void Main(string[] args)
{
Console.WriteLine("主线程启动,线程ID:{0}", Thread.CurrentThread.ManagedThreadId);
try
{
Parallel.Invoke(() => Run1("task1"), () => Run2("task2"), () => Run3("task3"));
}
catch (AggregateException ex)
{
//AggregateException捕获并行产生的一组异常集合
foreach (var item in ex.InnerExceptions)
{
Console.WriteLine(item);
};
}
Console.WriteLine("主线程结束,线程ID:{0}", Thread.CurrentThread.ManagedThreadId);
Console.ReadKey();
}

Invoke方法中调用了一个产生异常的方法,但是结果显示异常并不会影响其它方法及主线程的执行

ParallelOptions类

CancellationToken

获取或设置与此 ParallelOptions 实例关联的 CancellationToken

MaxDegreeOfParallelism

获取或设置此 ParallelOptions 实例所允许的并发任务的最大数目。

并行开发-Paraller的更多相关文章

  1. 并行开发——Parallel的使用 -摘自网络

    随着多核时代的到来,并行开发越来越展示出它的强大威力,像我们这样的码农再也不用过多的关注底层线程的实现和手工控制, 要了解并行开发,需要先了解下两个概念:“硬件线程”和“软件线程”. 1. 硬件线程 ...

  2. 并行开发 1.Parallel

    原文:8天玩转并行开发——第一天 Parallel的使用 随着多核时代的到来,并行开发越来越展示出它的强大威力,像我们这样的码农再也不用过多的关注底层线程的实现和手工控制, 要了解并行开发,需要先了解 ...

  3. [.net 面向对象程序设计进阶] (24) 团队开发利器(三)使用SVN多分支并行开发(下)

    [.net 面向对象程序设计进阶] (24) 团队开发利器(三)使用SVN多分支并行开发(下) 本篇导读: 接上篇继续介绍SVN的高级功能,即使用分支并行开发.随着需求的不断变更,新功能的增加.特别是 ...

  4. .NET下的并行开发(案例代码)

    以下主要是通过一个报表处理程序来说明并行开发的方式.对于数据冲突和共享,可以通过对象数组解决.设计到并行的核心代码已用红色标出.在并行程序的处理上,需要把原来串行的子公司变成一个一个类的对象,让所有的 ...

  5. .NET下的并行开发

    并行开发一直是程序员在开发项目中遇到的一道坎,但为了迎合硬件的升级,面对高端多核的处理器,并行编程势在必行.在.NET平台下的开发支持并行模式,下面用一个实际项目说明并行的高效率和神奇之处. 在优化中 ...

  6. 【翻译】CEDEC2014跨世代多平台并行开发PS4版如龙维新开发的一年

    本篇PPT讲述的是如龙4的开发过程中,集中在PS3和PS4并行开发中所遇到和解决的一些问题.如64位指针,DX9向DX11移植API的问题,以及在PS4上使用并行渲染在1080P下让FPS达到60等. ...

  7. 8天玩转并行开发——第一天 Parallel的使用

    转自:http://www.cnblogs.com/huangxincheng/archive/2012/04/02/2429543.html 随着多核时代的到来,并行开发越来越展示出它的强大威力,像 ...

  8. C# 并行开发总结

    本文内容 均参考自 <C#并行高级编程> TPL 支持 数据并行(有大量数据要处理,必须对每个数据执行同样的操作, 任务并行(有好多可以并发运行的操作),流水线(任务并行和数据并行的结合体 ...

  9. 并行开发学习随笔1——plinq并行

    这两天在看园友的文章 <8天玩转并行开发——第三天 plinq的使用> 对里面的第一个实例亲手实践了一下,发现了一点有意思的事情. 测试环境:.net 4.5 64位(如果是32位的,测试 ...

随机推荐

  1. 哪些 Python 库让你相见恨晚?【转】

    原文链接:https://www.zhihu.com/question/24590883/answer/92420471 原文链接:Python 资源大全 ---------------- 这又是一个 ...

  2. Open Graph Protocol(开放内容协议)

    最近在整理公司hexo博客的时候突然发现在页面 head 里面有一个这个奇怪的 meta Open Graph Protocol(开放内容协议) 开放内容协议一种新的HTTP头部标记,即这种协议可以让 ...

  3. Spring Framework 5.x 学习专栏

    Spring Framework 5.0 入门篇 Spring构建REST Web Service 消费一个RESTful Web Service 事务管理 Spring使用JDBC访问关系数据 任务 ...

  4. Atitit s2018.2 s2 doc list on home ntpc.docx  \Atiitt uke制度体系 法律 法规 规章 条例 国王诏书.docx \Atiitt 手写文字识别 讯飞科大 语音云.docx \Atitit 代码托管与虚拟主机.docx \Atitit 企业文化 每日心灵 鸡汤 值班 发布.docx \Atitit 几大研发体系对比 Stage-Gat

    Atitit s2018.2 s2 doc list on home ntpc.docx \Atiitt uke制度体系  法律 法规 规章 条例 国王诏书.docx \Atiitt 手写文字识别   ...

  5. Elasticsearch常用配置及性能参数[转]

    cluster.name: estest   集群名称node.name: “testanya”  节点名称 node.master: false  是否主节点node.data: true   是否 ...

  6. vue-router的router.go(n)问题?

    <template> <div> <mt-navbar v-model="selected" class="container" ...

  7. Git入门到高级系列2-git高级操作

    视频课程地址 腾讯课堂 git 清理 git clean命令用来从你的工作目录中删除所有没有tracked过的文件. 命令 说明 git clean -n 告诉你哪些文件会被删除. 记住他不会真正的删 ...

  8. Spark性能优化指南——基础篇

    本文转自:http://tech.meituan.com/spark-tuning-basic.html 感谢原作者 前言 在大数据计算领域,Spark已经成为了越来越流行.越来越受欢迎的计算平台之一 ...

  9. 利用SEH防范BP(int 3)断点

    利用SEH技术实现反跟踪,这个方法比单纯用判断API函数第一个字节是否为断点更加有效,可以防止在API函数内部的多处地址设置断点 通过int 3指令故意产生一个异常,从而让系统转入自己的异常处理函数, ...

  10. Hive学习笔记——安装和内部表CRUD

    1.首先需要安装Hadoop和Hive 安装的时候参考 http://blog.csdn.net/jdplus/article/details/46493553 安装的版本是apache-hive-2 ...