【C#】线程之Parallel

时间:2022-09-18 14:53:31

  在一些常见的编程情形中,使用任务也许能提升性能。为了简化变成,静态类System.Threading.Tasks.Parallel封装了这些常见的情形,它内部使用Task对象。

Parallel.For & Parallel.Foreach & Pararllel.Invoke 

           Parallel.For(, , (i) =>
{
//i是从0开始一直到1000结束 }); var lst = new List<string>(); Parallel.ForEach(lst, (s) =>
{
//do something
}); //Invoke
Parallel.Invoke(
() => { },
() => { },
() => { }
);

三个不同的方式做并行操作,Invoke是自己定义的并行,像上面的代码,只会开三个线程去做,而For和Foreach和普通的没什么区别只是并行去做了.

这里需要注意的是:这个三个方法都会堵塞当前线程,要等待所有线程都做完了以后才能往下执行,这是有用的,当进行数据分析的时候,每条数据都是独立的,这个时候等待所有线程做完是有意义的,而如果需要不堵塞线程可以用TASK来包一层,具体请参照【C#】线程之Task。在并发的时候如果有某些线程出现了异常,这个时候不会中断线程,会在所有线程结束的时候抛出AggregateException(并不是所有异常都能用这个捕获),我们通过这个异常可以知道所有的异常:

           Parallel.ForEach(new List<string>
{
"aa",
"bb",
"cc",
"dd",
"ee",
}, (b) =>
{
Thread.Sleep();
throw new Exception(b);
});
}
catch (AggregateException ex)
{
foreach (var exception in ex.Flatten().InnerExceptions)
{
Console.WriteLine(exception.Message);
} }

ParallelOptions

看一下官方给出的解释:

  

    public class ParallelOptions
{ public ParallelOptions(); //允许取消操作
public CancellationToken CancellationToken { get; set; } //允许指定可以并发操作的最大工作项目数
public int MaxDegreeOfParallelism { get; set; } //允许指定要使用哪个TaskScheduler
public TaskScheduler TaskScheduler { get; set; }
}

这个类提供我们最大并发数量的设置,取消操作的Token赋值,以及任务调度器的设置(关于这个,需要另拉一章来说,此处只关注前面两个)

        //定义线程取消的一个对象
var cancel = new CancellationTokenSource(); var po = new ParallelOptions()
{
CancellationToken = cancel.Token,
MaxDegreeOfParallelism = 2,
};
//为了不堵塞线程,这里开启一个线程
Task.Run(() =>
{
try
{
Parallel.For(0, 1000, po, (i) =>
{
Thread.Sleep(1000);
po.CancellationToken.ThrowIfCancellationRequested();
Console.WriteLine(i);
});
}
catch (AggregateException ex)
{ foreach (var e in ex.Flatten().InnerExceptions)
{
Console.WriteLine(e.Message);
}
}
catch (Exception ex)
{
//OperationCanceledException
Console.WriteLine(ex.GetType().Name);
}
//不设置取消的TOKEN
}, CancellationToken.None); Thread.Sleep(10 * 1000);
cancel.Cancel();

线程最大并行数位2个,如果取消的话,整个Parallel会全部取消,并且抛出OperationCanceledException异常。

Parallel.For & Parallel.Foreach的重载

  我只挑选了一个重载来说

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

  

来看一下官方的解释:

   localInit:任务局部初始化,为参与工作的每一个任务都调用一次该委托这个委托在任务被要求处理一个工作项之前调用的。

   boy:为参与工作的各个线程锁处理的每一项都调用一次该委托

localFinally:任务局部终结委托,为参与工作的每一个任务都调用一次该委托,这个委托是在任务处理好派给它的所有工作项之后调用的。即使主体委托代码引发一个未处理的异常,也会调用它。

    

public static void Parallel_For_Local_Test()

{
int[] nums = Enumerable.Range(, ).ToArray<int>(); long total = ; ParallelLoopResult result = Parallel.For<long>(, nums.Length,
() => { return ; },
(j, loop, subtotal) =>
{
// 延长任务时间,更方便观察下面得出的结论 Thread.SpinWait(); Console.WriteLine("当前线程ID为:{0},j为{1},subtotal为:{2}。" , Thread.CurrentThread.ManagedThreadId, j.ToString(), subtotal.ToString()); if (j == ) loop.Break(); if (j > loop.LowestBreakIteration)
{
Thread.Sleep();
Console.WriteLine("j为{0},等待4s种,用于判断已开启且大于阻断迭代是否会运行完。", j.ToString());
}
Console.WriteLine("j为{0},LowestBreakIteration为:{1}", j.ToString(), loop.LowestBreakIteration);
subtotal += nums[j];
return subtotal;
},
(finalResult) => Interlocked.Add(ref total, finalResult)
); Console.WriteLine("total值为:{0}", total.ToString());
if (result.IsCompleted)
Console.WriteLine("循环执行完毕");
else
Console.WriteLine("{0}"
, result.LowestBreakIteration.HasValue ? "调用了Break()阻断循环." : "调用了Stop()终止循环."); }

  看一下输出【C#】线程之Parallel

分析一下:

a)        泛型类型参数TLocal为本地线程数据类型,本示例设置为long。

b)        三个委托的参数解析body(j, loop, subtotal):首先初始委托localInit中返回了0,所以body委托中参数subtotal的初始值即为0,body委托的参数j对应的是当前迭代索引,参数loop为当前迭代状态ParallelLoopState对象;localFinally委托参数为body委托的返回值。

c)        三个委托三个阶段中都可能并行运行,因此您必须同步对任何共享变量的访问,如示例中在finally委托中使用了System.Threading.Interlocked对象。

d)        在索引为23的迭代中调用Break()后:

i.              索引小于23的所有迭代仍会运行(即使还未开始处理),并在退出循环之前处理完。

ii.              索引大于 23 的迭代若还未开启则会被放弃;若已处于运行中则会在退出循环之前处理完。

e)        对于调用Break()之后,在任何循环迭代中访问LowestBreakIteration属性都会返回调用Break()的迭代对应的索引。

                                                                注:滴答的雨(何雨泉)的帮助

ParallelLoopState

    可用来使 Tasks.Parallel 循环的迭代与其他迭代交互,并为 Parallel 类的循环提供提前退出循环的功能。此类的实例不要自行创建,它由 Parallel 类创建并提供给每个循环项,并且只应该在提供此实例的“循环内部”使用。

  

public class ParallelLoopState

{

    // 获取循环的任何迭代是否已引发相应迭代未处理的异常。
public bool IsExceptional { get; } // 获取循环的任何迭代是否已调用 ParallelLoopState.Stop()。
public bool IsStopped { get; } // 获取在Parallel循环中调用 ParallelLoopState.Break() 的最低循环迭代。
public long? LowestBreakIteration { get; } // 获取循环的当前迭代是否应基于此迭代或其他迭代发出的请求退出。
public bool ShouldExitCurrentIteration { get; } //通知Parallel循环当前迭代”之后”的其他迭代不需要运行。
public void Break(); //通知Parallel循环当前迭代“之外”的所有其他迭代不需要运行。
public void Stop();
}

  

Break()

Break()用于通知Parallel循环当前迭代“之后”的其他迭代不需要运行。例如,对于从 0 到 1000 并行迭代的 for 循环,如果在第 100 次迭代调用 Break(),则低于 100 的所有迭代仍会运行(即使还未开始处理),并在退出循环之前处理完。从 101 到 1000 中还未开启的迭代则会被放弃。

对于已经在执行的长时间运行迭代,Break()将为已运行还未结束的迭代对应ParallelLoopResult结构的LowestBreakIteration属性设置为调用Bread()迭代项的索引。

Stop()

Stop() 用于通知Parallel循环当前迭代“之外”的所有其他迭代不需要运行,无论它们是位于当前迭代的上方还是下方。

对于已经在执行的长时间运行迭代,可以检查 IsStopped属性,在观测到是 true 时提前退出。

Stop 通常在基于搜索的算法中使用,在找到一个结果之后就不需要执行其他任何迭代。(比如在看视频或漫画时自动匹配响应最快的服务器)

ShouldExitCurrentIteration 属性

当循环的迭代调用 Break 或 Stop时,或一个迭代引发异常,或取消循环时,Parallel 类将主动尝试禁止开始执行循环的其他迭代。但是,可能有无法阻止其他迭代启动的情况。也可能是长时间运行的迭代已经开始执行的情况。在此类情况下,迭代可以通过显式检查 ShouldExitCurrentIteration 属性,在该属性返回 true 时停止执行。

  

   LowestBreakIteration 属性

  返回过程中调用过Break方法的最低的项,如果从来没有调用过Break则返回null.

 

这里还有一个好玩的地方, 我们知道Parallel.For & Parallel.Foreach会返回一个ParallelLoopResult类型的对象,我们可以通过IsComplete()来判断是否结束并发,但是如果我们用了ParallelLoopState的Break(), 这个时候IsComplete()返回的是false,并且LowestBreakIteration属性不为null,如果调用的是Stop(), IsComplete()返回的是true,且LowestBreakIteration属性为null.

【C#】线程之Parallel的更多相关文章

  1. 并行编程多线程之Parallel

    1.简介 随着多核时代的到来,并行开发越来越展示出它的强大威力!使用并行程序,充分的利用系统资源,提高程序的性能.在.net 4.0中,微软给我们提供了一个新的命名空间:System.Threadin ...

  2. C&num;多线程之Parallel中 类似于for的continue,break的方法

    好久没写东西了,终于找到点知识记录下... 利用ParallelLoopState对象来控制Parallel.For函数的执行,ParallelLoopState对象是由运行时在后台创建的: Para ...

  3. 多线程之Parallel类

    Parallel类是对线程的一个抽象.该类位于System.Threading.Tasks名称空间中,提供了数据和任务并行性. Paraller类定义了数据并行地For和ForEach的静态方法,以及 ...

  4. iOS多线程之8&period;NSOPeration的其他用法

      本文主要对NSOPeration的一些重点属性和方法做出介绍,以便大家可以更好的使用NSOPeration. 1.添加依赖 - (void)addDependency:(NSOperation * ...

  5. python 线程之 threading&lpar;四)

    python 线程之 threading(三) http://www.cnblogs.com/someoneHan/p/6213100.html中对Event做了简单的介绍. 但是如果线程打算一遍一遍 ...

  6. python 线程之 threading&lpar;三)

    python 线程之 threading(一)http://www.cnblogs.com/someoneHan/p/6204640.html python 线程之 threading(二)http: ...

  7. python 线程之&lowbar;thread

    python 线程之_thread _thread module: 基本用法: def child(tid): print("hello from child",tid) _thr ...

  8. Java多线程之ConcurrentSkipListMap深入分析&lpar;转&rpar;

    Java多线程之ConcurrentSkipListMap深入分析   一.前言 concurrentHashMap与ConcurrentSkipListMap性能测试 在4线程1.6万数据的条件下, ...

  9. iOS多线程之GCD小记

    iOS多线程之GCD小记 iOS多线程方案简介 从各种资料中了解到,iOS中目前有4套多线程的方案,分别是下列4中: 1.Pthreads 这是一套可以在很多操作系统上通用的多线程API,是基于C语言 ...

随机推荐

  1. jvm内存区域

    概述 jvm内存分为几个区域: 程序计数器 虚拟机栈 本地方法栈 堆 方法区 运行时常量池 直接内存 这些内存区域是在java进程中细分的,为java程序提供服务 不同的区域存储的内容不一样,生命周期 ...

  2. 本周psp个人作业

    计划--用一天的时间来做这个项目 需求分析--作为一个观众,我想要知道每局的比分,以便我更了解比赛情况. 生成设计文档--用类图来进行说明. 设计复审---无 代码规范--3H 具体设计--建立数据库 ...

  3. Bean不同配置方式的比较

    在<Spring3.x 企业应用开发实战>上学习了Bean的三种不同配置方法,下图是我从书中截取的图片,比较了一下这三种配置的异同 ps:发现图片不能完全显示(右侧有一块不显示),解决方法 ...

  4. C&num; 异常处理 &lt&semi;-&gt&semi; 连接远程数据库遇到的问题

    PS : 移植类库需要重新生成解决方案:(要不然不能将类库添加进项目)

  5. &lpar;转&rpar;eclipse安装ADT插件重启后不显示Android SDK Manager和Android Virtual Device Manager图标的一种解决办法

    文章来源:http://blog.csdn.net/zcyhappy1314/article/details/8307534 下面说的这种情况是在正确安装ADT插件的前提下,重启eclipse后,工具 ...

  6. Android FragmentActivity&plus;viewpager的使用

    使用场景,打算设计一个“底部菜单栏+其余可滑动的页面”的简单的功能. package com.lanyuweng.mibaby; import android.content.Intent; impo ...

  7. Sublime Text 超好用的侧栏插件SideBarEnhancements

    SideBarEnhancements插件有效地改进了Sublime Text的侧边栏.安装插件后在侧边栏上点击右键,可以找到一下新功能:在资源管理器中打开.新建文件.新建文件夹.以…打开.在浏览器中 ...

  8. 第十八篇 ANDROID的声音管理系统及服务

     声音管理系统用来实现声音的输入和输出.声音的控制和路由等功能,包括主和各种音源的音量调节.声音焦点控制,声音外设的检测和状态管理,声音源输入和输出的策略管理.音效的播放.音轨设置和播放.录音设置 ...

  9. mysql兼容emoji表情存取

    emoji介绍 Emoji (絵文字,词义来自日语えもじ,e-moji,moji在日语中的含义是字符)是一套起源于日本的12x12像素表情符号,由栗田穣崇(Shigetaka Kurit)创作,最早在 ...

  10. qml&colon; QtChart横纵轴label设置;

    在qml中,使用ChartView作为图表展示区域, 但是并没有给定接口用来设置xlabel,ylabel. 没得办法,只能采用笨方案: (我的方法如下) import QtQuick 2.0 imp ...