说到.net中的并行编程,也许你的第一反应就是Task,确实Task是一个非常灵活的用于并行编程的一个专用类,不可否认越灵活的东西用起来就越

复杂,高度封装的东西用起来很简单,但是缺失了灵活性,这篇我们就看看这些好用但灵活性不高的几个并行方法。

一:Invoke

  现在电子商务的网站都少不了订单的流程,没有订单的话网站也就没有存活的价值了,往往在订单提交成功后,通常会有这两个操作,第一个:发起

信用卡扣款,第二个:发送emial确认单,这两个操作我们就可以在下单接口调用成功后,因为两个方法是互不干扰的,所以就可以用invoke来玩玩了。

         static void Main(string[] args)
{
Parallel.Invoke(Credit, Email); Console.Read();
} static void Credit()
{
Console.WriteLine("****************** 发起信用卡扣款中 ******************"); Thread.Sleep(); Console.WriteLine("扣款成功!");
} static void Email()
{
Console.WriteLine("****************** 发送邮件确认单!*****************"); Thread.Sleep(); Console.WriteLine("email发送成功!");
}

怎么样,实现起来是不是很简单,只要把你需要的方法塞给invoke就行了,不过在这个方法里面有一个重载参数需要注意下,

  public static void Invoke(ParallelOptions parallelOptions, params Action[] actions);

有时候我们的线程可能会跑遍所有的内核,为了提高其他应用程序的稳定性,就要限制参与的内核,正好ParallelOptions提供了

MaxDegreeOfParallelism属性。

好了,下面我们大概翻翻invoke里面的代码实现,发现有几个好玩的地方:

<1>: 当invoke中的方法超过10个话,我们发现它走了一个internal可见的ParallelForReplicatingTask的FCL内部专用类,而这个类是继承自

   Task的,当方法少于10个的话,才会走常规的Task.

<2> 居然发现了一个装exception 的ConcurrentQueue<Exception>队列集合,多个异常入队后,再包装成AggregateException抛出来。

比如:throw new AggregateException(exceptionQ);

<3> 我们发现,不管是超过10个还是小于10个,都是通过WaitAll来等待所有的执行,所以缺点就在这个地方,如果某一个方法执行时间太长

   不能退出,那么这个方法是不是会长期挂在这里不能出来,也就导致了主流程一直挂起,然后页面就一直挂起,所以这个是一个非常危险

的行为,如果我们用task中就可以在waitall中设置一个过期时间,但invoke却没法做到,所以在使用invoke的时候要慎重考虑。

     try
{
if (actionsCopy.Length > || (parallelOptions.MaxDegreeOfParallelism != - && parallelOptions.MaxDegreeOfParallelism < actionsCopy.Length))
{
ConcurrentQueue<Exception> exceptionQ = null;
try
{
int actionIndex = ;
ParallelForReplicatingTask parallelForReplicatingTask = new ParallelForReplicatingTask(parallelOptions, delegate
{
for (int l = Interlocked.Increment(ref actionIndex); l <= actionsCopy.Length; l = Interlocked.Increment(ref actionIndex))
{
try
{
actionsCopy[l - ]();
}
catch (Exception item)
{
LazyInitializer.EnsureInitialized<ConcurrentQueue<Exception>>(ref exceptionQ, () => new ConcurrentQueue<Exception>());
exceptionQ.Enqueue(item);
}
if (parallelOptions.CancellationToken.IsCancellationRequested)
{
throw new OperationCanceledException(parallelOptions.CancellationToken);
}
}
}, TaskCreationOptions.None, InternalTaskOptions.SelfReplicating);
parallelForReplicatingTask.RunSynchronously(parallelOptions.EffectiveTaskScheduler);
parallelForReplicatingTask.Wait();
}
catch (Exception ex2)
{
LazyInitializer.EnsureInitialized<ConcurrentQueue<Exception>>(ref exceptionQ, () => new ConcurrentQueue<Exception>());
AggregateException ex = ex2 as AggregateException;
if (ex != null)
{
using (IEnumerator<Exception> enumerator = ex.InnerExceptions.GetEnumerator())
{
while (enumerator.MoveNext())
{
Exception current = enumerator.Current;
exceptionQ.Enqueue(current);
}
goto IL_264;
}
}
exceptionQ.Enqueue(ex2);
IL_264:;
}
if (exceptionQ != null && exceptionQ.Count > )
{
Parallel.ThrowIfReducableToSingleOCE(exceptionQ, parallelOptions.CancellationToken);
throw new AggregateException(exceptionQ);
}
}
else
{
Task[] array = new Task[actionsCopy.Length];
if (parallelOptions.CancellationToken.IsCancellationRequested)
{
throw new OperationCanceledException(parallelOptions.CancellationToken);
}
for (int j = ; j < array.Length; j++)
{
array[j] = Task.Factory.StartNew(actionsCopy[j], parallelOptions.CancellationToken, TaskCreationOptions.None, InternalTaskOptions.None, parallelOptions.EffectiveTaskScheduler);
}
try
{
if (array.Length <= )
{
Task.FastWaitAll(array);
}
else
{
Task.WaitAll(array);
}
}
catch (AggregateException ex3)
{
Parallel.ThrowIfReducableToSingleOCE(ex3.InnerExceptions, parallelOptions.CancellationToken);
throw;
}
finally
{
for (int k = ; k < array.Length; k++)
{
if (array[k].IsCompleted)
{
array[k].Dispose();
}
}
}
}
}
finally
{
if (TplEtwProvider.Log.IsEnabled())
{
TplEtwProvider.Log.ParallelInvokeEnd((task != null) ? task.m_taskScheduler.Id : TaskScheduler.Current.Id, (task != null) ? task.Id : , forkJoinContextID);
}
}

二:For

下面再看看Parallel.For,我们知道普通的For是一个串行操作,如果说你的for中每条流程都需要执行一个方法,并且这些方法可以并行操作且

比较耗时,那么为何不尝试用Parallel.For呢,就比如下面的代码。

 class Program
{
static void Main(string[] args)
{
List<Action> actions = new List<Action>() { Credit, Email }; var result = Parallel.For(, actions.Count, (i) =>
{
actions[i]();
}); Console.WriteLine("执行状态:" + result.IsCompleted); Console.Read();
} static void Credit()
{
Console.WriteLine("****************** 发起信用卡扣款中 ******************"); Thread.Sleep(); Console.WriteLine("扣款成功!");
} static void Email()
{
Console.WriteLine("****************** 发送邮件确认单!*****************"); Thread.Sleep(); Console.WriteLine("email发送成功!");
}
}

下面我们再看看Parallel.For中的最简单的重载和最复杂的重载:

 public static ParallelLoopResult For(int fromInclusive, int toExclusive, Action<int> body);

 public static ParallelLoopResult For<TLocal>(int fromInclusive, int toExclusive, ParallelOptions parallelOptions, Func<TLocal> localInit, Func<int, ParallelLoopState, TLocal, TLocal> body, Action<TLocal> localFinally);
  

<1> 简单的重载不必多说,很简单,我上面的例子也演示了。

<2> 最复杂的这种重载提供了一个AOP的功能,在每一个body的action执行之前会先执行localInit这个action,在body之后还会执行localFinally

这个action,有没有感觉到已经把body切成了三块?好了,下面看一个例子。

     static void Main(string[] args)
{
var list = new List<int>() { , , , }; var options = new ParallelOptions(); var total = ; var result = Parallel.For(, list.Count, () =>
{
Console.WriteLine("------------ thead --------------"); return ;
},
(i, loop, j) =>
{
Console.WriteLine("------------ body --------------"); Console.WriteLine("i=" + list[i] + " j=" + j); return list[i];
},
(i) =>
{
Console.WriteLine("------------ tfoot --------------"); Interlocked.Add(ref total, i); Console.WriteLine("total=" + total);
}); Console.WriteLine("iscompleted:" + result.IsCompleted);
Console.Read();
}

接下来我们再翻翻它的源代码,由于源码太多,里面神乎其神,我就找几个好玩的地方。

<1>  我在里面找到了一个rangeManager分区函数,代码复杂看不懂,貌似很强大。

         internal RangeManager(long nFromInclusive, long nToExclusive, long nStep, int nNumExpectedWorkers)
{
this.m_nCurrentIndexRangeToAssign = ;
this.m_nStep = nStep;
if (nNumExpectedWorkers == )
{
nNumExpectedWorkers = ;
}
ulong num = (ulong)(nToExclusive - nFromInclusive);
ulong num2 = num / (ulong)((long)nNumExpectedWorkers);
num2 -= num2 % (ulong)nStep;
if (num2 == 0uL)
{
num2 = (ulong)nStep;
}
int num3 = (int)(num / num2);
if (num % num2 != 0uL)
{
num3++;
}
long num4 = (long)num2;
this.m_indexRanges = new IndexRange[num3];
long num5 = nFromInclusive;
for (int i = ; i < num3; i++)
{
this.m_indexRanges[i].m_nFromInclusive = num5;
this.m_indexRanges[i].m_nSharedCurrentIndexOffset = null;
this.m_indexRanges[i].m_bRangeFinished = ;
num5 += num4;
if (num5 < num5 - num4 || num5 > nToExclusive)
{
num5 = nToExclusive;
}
this.m_indexRanges[i].m_nToExclusive = num5;
}
}

<2> 我又找到了这个神奇的ParallelForReplicatingTask类。

那么下面问题来了,在单线程的for中,我可以continue,可以break,那么在Parallel.For中有吗?因为是并行,所以continue基本上就没有

存在价值,break的话确实有价值,这个就是委托中的ParallelLoopState做到的,并且还新增了一个Stop。

三:ForEach

其实ForEach和for在本质上是一样的,你在源代码中会发现在底层都是调用一个方法的,而ForEach会在底层中调用for共同的函数之前还会执行

其他的一些逻辑,所以这就告诉我们,能用Parallel.For的地方就不要用Parallel.ForEach,其他的都一样了,这里就不赘述了。

看看Parallel中高度封装的三个方法,Invoke,For和ForEach的更多相关文章

  1. Android中全屏 取消标题栏,TabHost中设置NoTitleBar的三种方法(转)

    Android中全屏 取消标题栏,TabHost中设置NoTitleBar的三种方法http://www.cnblogs.com/zdz8207/archive/2013/02/27/android- ...

  2. Openerp 中打开 URL 的三种 方法

    来自:http://shine-it.net/index.php/topic,8013.0.html 最近总结了,Openerp 中打开 URL 的三种 方法: 一.在form view 添加 < ...

  3. mysql 中添加索引的三种方法

    原文:http://www.andyqian.com/2016/04/06/database/mysqleindex/ 在mysql中有多种索引,有普通索引,全文索引,唯一索引,多列索引,小伙伴们可以 ...

  4. jQuery中detach&&remove&&empty三种方法的区别

    jQuery中empty&&remove&&detach三种方法的区别 empty():移除指定元素内部的所有内容,但不包括它本身 remove():移除指定元素内部的 ...

  5. mfc 在VC的两个对话框类中传递参数的三种方法

    弄了好久,今天终于把在VC中的对话框类之间传递参数的问题解决了,很开心,记录如下: 1. 我所建立的工程是一个基于MFC对话框的应用程序,一共有三个对话框,第一个对话框为主对话框,所对应的类为CTMD ...

  6. cocos2dx中创建动画的三种方法

    1.最最原始的方法,先创建动画帧,再创建动画打包(animation),再创建动画(animate) 第一步: 创建动画帧:CCSpriteFrame,依赖于原始的资源图片(xx.png,xx.jpg ...

  7. ASP.NET中身份验证的三种方法

    Asp.net的身份验证有有三种,分别是"Windows | Forms | Passport",其中又以Forms验证用的最多,也最灵活.Forms 验证方式对基于用户的验证授权 ...

  8. JS模拟实现封装的三种方法

      前  言  继承是使用一个子类继承另一个父类,那么子类可以自动拥有父类中的所有属性和方法,这个过程叫做继承!  JS中有很多实现继承的方法,今天我给大家介绍其中的三种吧. 1.在 Object类上 ...

  9. vue后台管理项目中菜单栏切换的三种方法

    第一种方法:vue嵌套路由(二) <el-menu :default-active="defaultActive" style="min-height: 100%; ...

随机推荐

  1. C#项目中常用到的设计模式

    1. 引言 一个项目的通常都是从Demo开始,不断为项目添加新的功能以及重构,也许刚开始的时候代码显得非常凌乱,毫无设计可言.但是随着项目的迭代,往往需要将很多相同功能的代码抽取出来,这也是设计模式的 ...

  2. 电子商务中:B2C、B2B、C2B、C2C、O2O、P2P

     c2c实际是电子商务的专业用语,是个人与个人之间的电子商务.比如一个消费者有一台电脑,通过网络进行交易,把它出售给另外一个消费者,此种交易类型就称为C2C电子商务.淘宝是属于C2C模式的.   c2 ...

  3. Scalaz(54)- scalaz-stream: 函数式多线程编程模式-Free Streaming Programming Model

    长久以来,函数式编程模式都被认为是一种学术研究用或教学实验用的编程模式.直到近几年由于大数据和多核CPU的兴起造成了函数式编程模式在一些实际大型应用中的出现,这才逐渐改变了人们对函数式编程无用论的观点 ...

  4. mysql 5.7.15 vs mysql 5.6.31性能测试以及不同linux内核性能比较

    最近,将部分开发和测试环境的mysql升级到5.7之后,今天抽时间测试了下5.6和5.7 PK查询的性能,使用mysqlslap进行测试,测试结果发现在低配下,percona 5.6.31大约比5.7 ...

  5. Redis-分片

    分片(partitioning)就是将你的数据拆分到多个 Redis 实例的过程,这样每个实例将只包含所有键的子集.本文第一部分将向你介绍分片的概念,第二部分将向你展示 Redis 分片的可选方案. ...

  6. 探秘Java中的String、StringBuilder以及StringBuffer

    探秘Java中String.StringBuilder以及StringBuffer 相信String这个类是Java中使用得最频繁的类之一,并且又是各大公司面试喜欢问 到的地方,今天就来和大家一起学习 ...

  7. Spring4学习笔记 - Bean的生命周期

    1 Spring IOC 容器对 Bean 的生命周期进行管理的过程: 1)通过构造器或工厂方法创建 Bean 实例 2)为 Bean 的属性设置值和对其他 Bean 的引用 3)调用 Bean 的初 ...

  8. 简单可用好实现的 HA 高可用设计

    本文为作者原创,如需转载请注明出处. 1. 实现的功能 一主多备,自动选主 启动记录可查询 2. 前置需求 一台数据库用以记录,如 MySQL.Redis.MongoDB 等.关键是设计中的思想,用啥 ...

  9. angular学习的一些小笔记(中)之表单验证

    表单验证 我去,我感觉我这个人其实还是一个很傻逼的一个人,老是因为拼错了一个单词或者怎么样就浪费我很长时间,这样真的不行不行,要正确对待这个问题,好了,说正题吧,angular也有表单验证minlen ...

  10. slid.es – 创建在线幻灯片和演示文稿的最佳途径

    slid.es 提供了一种创建在线幻灯片和演示文稿的简单方法,让你通过几个简单的步骤制作效果精美的在线演示文稿.基于 HTML5 和 CSS3 实现,在现代浏览器中效果最佳. 您可能感兴趣的相关文章 ...