并行开发的概念

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

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. iOS 出现内存泄漏的几种原因

    一.从AFNet 对于iOS开发者,网络请求类AFNetWorking是再熟悉不过了,对于AFNetWorking的使用我们通常会对通用参数.网址环境切换.网络状态监测.请求错误信息等进行封装.在封装 ...

  2. Java并发编程:4种线程池和缓冲队列BlockingQueue

    一. 线程池简介 1. 线程池的概念: 线程池就是首先创建一些线程,它们的集合称为线程池.使用线程池可以很好地提高性能,线程池在系统启动时即创建大量空闲的线程,程序将一个任务传给线程池,线程池就会启动 ...

  3. mysql 动态增加列,查找表中有多少列,具体什么列。 通过JSON生成mysql表 支持子JSON

    好消息, 程序员专用早餐机.和掌柜说 ideaam,可以节省20元. 点击链接  或復·制这段描述¥k3MbbVKccMU¥后到淘♂寳♀ 或者 淘宝扫码 支持下同行哈 ---------------- ...

  4. Linux报swap空间占用过高,但物理内存还有空余

    收到报警,swap空间占用过高,登录到系统查看内存使用详情,看到物理内存还有很多未使用 问题分析 Swap配置对性能的影响分配太多的Swap空间会浪费磁盘空间,而Swap空间太少,则系统会发生错误.如 ...

  5. Canvas入门到高级详解(下)

    四. Canvas 开发库封装 4.1 封装常用的绘制函数 4.1.1 封装一个矩形 //思考:我们用到的矩形需要哪些绘制的东西呢? 矩形的 x.y坐标 矩形的宽高 矩形的边框的线条样式.线条宽度 矩 ...

  6. webstrom IDE 激活

    2017.2.27更新 选择“license server” 输入:http://idea.imsxm.com/ 2016.2.2 版本的破解方式: 安装以后,打开软件会弹出一个对话框:选择“lice ...

  7. 设计模式——proxy代理模式

    目录 概述 定义 角色 为什么会有代理模式? 应用场景 示例 静态代理 例子 动态代理 JDK中生成代理对象的API 代码示例: 代码示例2 Cglib代理 代码示例 AOP(AspectOrient ...

  8. ftrace:跟踪你的内核函数! | Linux 中国

    版权声明:本文为博主原创文章.未经博主同意不得转载. https://blog.csdn.net/F8qG7f9YD02Pe/article/details/79161135 ftrace 是一个 L ...

  9. python 序列化模块之 json 和 pickle

    JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式,采用完全独立于语言的文本格式,支持不同程序之间的数据转换.但是只能转换简单的类型如:(列表.字典.字符串. ...

  10. js中文输入法字符串截断

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...