说到.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. Android Volley框架的使用(1)

    在Android开发中,经常要通过HTTP请求访问网络.为了使通过HTTP请求访问网络的过程更加简单,2013年提出了新的HTTP通信框架--Volley.Volley使用起来非常简单,适用于网络访问 ...

  2. IOS 2D游戏开发框架 SpriteKit

    最近发现Xcode自带的2D游戏开发框架SpriteKit可以直接引入到APP中进行混合开发,这就是说可以开发出既带业务应用又带游戏的苹果APP,咋怎么觉得这是一个自己的小发现....呵呵....., ...

  3. Web应用网络模型

    Web应用网络模型 前言 这篇文章要介绍的是一个常见Web应用基本的过程跟网络模型,当然,对于多数的Client/Server应用也是适用的.延续这个系列文章的风格,只管通俗不管严谨. 概览 总体模型 ...

  4. jquery 集合操作

    修剪字符串 $.trim(value) 功能: 删除传入的字符串开头和结尾的空白 [空白]匹配js正则中的\s,也就是包括空白,换行,回车,制表符,换页以及Unicode字符\u00A0 返回值: 返 ...

  5. Spring4学习笔记2-配置Bean

    1.配置bean 配置形式:Xml和注解方式 Bean的配置方式:通过全类名(反射).工厂.FactoryBean 1.1 id必须唯一 2 Spring提供两种类型的IOC容器的实现 BeanFac ...

  6. ENVI软件操作【数据显示操作——Overlay菜单操作】

    一.注记层(Annotation) 注记层是ENVI的一个数据类型,它的后缀名是.ann.往往作为栅格数据层,矢量数据层.三维场景会绘图图表的附加数据叠加在上面,还可以作为镶嵌图像时候的裁剪线.注记数 ...

  7. 当jquery ajax遇上401请求

    jquery ajax是个很常用接口,而在请求时候,可能存在响应401的情况(身份认证过期或未登录),比较容易出现在混合应用上,如何进行身份认证,重发失败请求,还是值得注意的. ajax请求有两种方式 ...

  8. JS常用的设计模式

    单例模式 只创建类的唯一一个实例.我们看了好几种可以不通过构造函数和类Java语法达成单例的方法.从另一方面来说,JavaScript中所有的对象都是单例.有时候开发者说的单例是指通过模块化模式创建的 ...

  9. 学习zepto.js(原型方法)[1]

    新的一周,新的开始,今天来学习一下zepto里边的原型方法,就是通过$.进行调用的方法,也是可以通过$.fn进行扩展的方法: $.camelCase(): 方法接收一个字符串,将连字符格式的字符串转为 ...

  10. 如何对sharepoint图片库的文件夹的图片按照时间排序并分页显示

    /// <summary> /// 获取图片库第一层文件夹--根据文件夹名称排序 /// </summary> /// <param name="siteUrl ...