c# 异步进阶———— paralel [二]
前言
简单整理一下paralel,以上是并行的意思。
正文
我们在工作中常常使用task await 和 async,也就是将线程池进行了封装,那么还有一些更高级的应用。
是对task的封装,那么来看下paralel。
static void Main(string[] args)
{
var ints= Enumerable.Range(1, 100);
var result = Parallel.ForEach(ints, arg =>
{
Console.WriteLine(arg);
});
Console.Read();
}
可以看到结果是并行的。

那么来看下实现机制。
public static ParallelLoopResult ForEach<TSource>(IEnumerable<TSource> source, Action<TSource> body)
{
if (source == null)
{
throw new ArgumentNullException(nameof(source));
}
if (body == null)
{
throw new ArgumentNullException(nameof(body));
}
return ForEachWorker<TSource, object>(
source, s_defaultParallelOptions, body, null, null, null, null, null, null);
}
进行参数检验,然后交给了ForEachWorker。
这是一个基本的代码思路,就是复杂的方法中可以先校验参数,然后具体实现交给另外一个方法。
然后通过不同的类型,进行分类:

然后看下具体实现是什么?
进去看就是一个taskreplicator:

看下run在做什么。
public static void Run<TState>(ReplicatableUserAction<TState> action, ParallelOptions options, bool stopOnFirstFailure)
{
int maxConcurrencyLevel = (options.EffectiveMaxConcurrencyLevel > 0) ? options.EffectiveMaxConcurrencyLevel : int.MaxValue;
TaskReplicator replicator = new TaskReplicator(options, stopOnFirstFailure);
new Replica<TState>(replicator, maxConcurrencyLevel, CooperativeMultitaskingTaskTimeout_RootTask, action).Start();
Replica nextReplica;
while (replicator._pendingReplicas.TryDequeue(out nextReplica))
nextReplica.Wait();
if (replicator._exceptions != null)
throw new AggregateException(replicator._exceptions);
}
创建了一个taskreplictor,起到管理作用
然后创建了一个Replica,然后这个start 是关键
然后通过while,让每一个Replica 都运行完毕才推出,达到同步的效果
if (replicator._exceptions != null)
throw new AggregateException(replicator._exceptions);
可以看一下这个,这个是一个比较好的技巧。如果一个运行管理,不用抛出异常,之间在管理中进行运行处理总结。
比如结果,异常等。
那么就看下这个start。
protected Replica(TaskReplicator replicator, int maxConcurrency, int timeout)
{
_replicator = replicator;
_timeout = timeout;
_remainingConcurrency = maxConcurrency - 1;
_pendingTask = new Task(s => ((Replica)s).Execute(), this);
_replicator._pendingReplicas.Enqueue(this);
}
public void Start()
{
_pendingTask.RunSynchronously(_replicator._scheduler);
}
将会运行Execute,是同步的,而不是异步的,也就是说第一个task将会运行在当前线程。
那么看Execute在做什么?
public void Execute()
{
try
{
if (!_replicator._stopReplicating && _remainingConcurrency > 0)
{
CreateNewReplica();
_remainingConcurrency = 0; // new replica is responsible for adding concurrency from now on.
}
bool userActionYieldedBeforeCompletion;
ExecuteAction(out userActionYieldedBeforeCompletion);
if (userActionYieldedBeforeCompletion)
{
_pendingTask = new Task(s => ((Replica)s).Execute(), this, CancellationToken.None, TaskCreationOptions.None);
_pendingTask.Start(_replicator._scheduler);
}
else
{
_replicator._stopReplicating = true;
_pendingTask = null;
}
}
catch (Exception ex)
{
LazyInitializer.EnsureInitialized(ref _replicator._exceptions).Enqueue(ex);
if (_replicator._stopOnFirstFailure)
_replicator._stopReplicating = true;
_pendingTask = null;
}
}
一段一段分析:
if (!_replicator._stopReplicating && _remainingConcurrency > 0)
{
CreateNewReplica();
_remainingConcurrency = 0; // new replica is responsible for adding concurrency from now on.
}
这里当_replicator 也就是任务复制器没有停止的时候。这里有两种情况会停止,一种是任务完成,一种是任务异常且设置参数异常时候停止。
_remainingConcurrency 指的是副本数,默认是int.max。
那么就复制一个副本。
protected override void CreateNewReplica()
{
Replica<TState> newReplica = new Replica<TState>(_replicator, _remainingConcurrency, GenerateCooperativeMultitaskingTaskTimeout(), _action);
newReplica._pendingTask.Start(_replicator._scheduler);
}
复制完副本后,那么就开始运行我们的action了。
protected override void ExecuteAction(out bool yieldedBeforeCompletion)
{
_action(ref _state, _timeout, out yieldedBeforeCompletion);
}
这里传入了timeout,这个timeout并不是我们限制我们单个task的运行时间,而是当运行到一定时候后,这个task就停止运行,然后另外启动一个副本。
if (CheckTimeoutReached(loopTimeout))
{
replicationDelegateYieldedBeforeCompletion = true;
break;
}
if (userActionYieldedBeforeCompletion)
{
_pendingTask = new Task(s => ((Replica)s).Execute(), this, CancellationToken.None, TaskCreationOptions.None);
_pendingTask.Start(_replicator._scheduler);
}
else
{
_replicator._stopReplicating = true;
_pendingTask = null;
}
这个是为了符合操作系统的调度思想,跑的越久的,基本上优先级会低些。
那么看下这个_action主要在做什么吧。
while (myPartition.MoveNext())
{
KeyValuePair<long, TSource> kvp = myPartition.Current;
long index = kvp.Key;
TSource value = kvp.Value;
// Update our iteration index
if (state != null) state.CurrentIteration = index;
if (simpleBody != null)
simpleBody(value);
else if (bodyWithState != null)
bodyWithState(value, state);
else if (bodyWithStateAndIndex != null)
bodyWithStateAndIndex(value, state, index);
else if (bodyWithStateAndLocal != null)
localValue = bodyWithStateAndLocal(value, state, localValue);
else
localValue = bodyWithEverything(value, state, index, localValue);
if (sharedPStateFlags.ShouldExitLoop(index)) break;
// Cooperative multitasking:
// Check if allowed loop time is exceeded, if so save current state and return.
// The task replicator will queue up a replacement task. Note that we don't do this on the root task.
if (CheckTimeoutReached(loopTimeout))
{
replicationDelegateYieldedBeforeCompletion = true;
break;
}
}
就是拉取我们的enumerator的数据,然后simpleBody(value),进行运行我们写的action。
总结一下,其实Parallel 核心就是一个任务复制器,然后创建多个副本,拉取我们的数据,进行执行我们设置的action。
里面的主要功能,Parallel做到了限制副本数,因为我们知道task并不是越多越好。
第二个,如果长时间运行,那么Parallel是做了优化的,当达到timeout的时候,那么会重新启动一个副本(可以理解为一个线程)
第三点,Parallel 有一个foreach 进行迭代器的处理,这里不仅仅是让任务可以并行。
而且具备c# foreach的基本功能。
static void Main(string[] args)
{
var ints= Enumerable.Range(1, 100);
var result = Parallel.ForEach(ints, (arg, state)
=>
{
if (state.IsStopped)
{
return;
}
if (arg > 18)
{
state.Break();
}
});
if (result.IsCompleted)
{
Console.WriteLine("完成");
}
Console.Read();
}
可以进行中断。
还有一个函数,那就是stop,这个stop 比break 停止的快,break 要记录出,最小中断位置。

而stop 就是立马停止下来。
结
在上述中,我们知道可以传递一个taskschedule进行,那么这个taskschedule 是干什么的,对我们的任务调度有什么影响呢? 下一节,自我实现taskschedule。
c# 异步进阶———— paralel [二]的更多相关文章
- Android高手进阶教程(二十八)之---Android ViewPager控件的使用(基于ViewPager的横向相册)!!!
分类: Android高手进阶 Android基础教程 2012-09-14 18:10 29759人阅读 评论(35) 收藏 举报 android相册layoutobjectclassloade ...
- Bing Maps进阶系列二:使用GeocodeService进行地理位置检索
Bing Maps进阶系列二:使用GeocodeService进行地理位置检索 在<Bing Maps进阶系列一:初识Bing Maps地图服务>里已经对GeocodeService的功能 ...
- React Native 学习笔记--进阶(二)--动画
React Native 进阶(二)–动画 动画 流畅.有意义的动画对于移动应用用户体验来说是非常必要的.我们可以联合使用两个互补的系统:用于全局的布局动画LayoutAnimation,和用于创建更 ...
- Wireshark入门与进阶系列(二)
摘自http://blog.csdn.net/howeverpf/article/details/40743705 Wireshark入门与进阶系列(二) “君子生非异也,善假于物也”---荀子 本文 ...
- SpringBoot进阶教程(二十九)整合Redis 发布订阅
SUBSCRIBE, UNSUBSCRIBE 和 PUBLISH 实现了 发布/订阅消息范例,发送者 (publishers) 不用编程就可以向特定的接受者发送消息 (subscribers). Ra ...
- WPF 4 DataGrid 控件(进阶篇二)
原文:WPF 4 DataGrid 控件(进阶篇二) 上一篇<WPF 4 DataGrid 控件(进阶篇一)>中我们通过DataGridTemplateColumn 类自定义编辑 ...
- Spring Boot进阶系列二
上一篇文章,主要分析了怎么建立一个Restful web service,系列二主要创建一个H5静态页面使用ajax请求数据,功能主要有添加一本书,请求所有书并且按照Id降序排列,以及查看,删除一本书 ...
- Python进阶(十二)----re模块
Python进阶(十二)----re模块 一丶re模块 re模块是python将正则表达式封装之后的一个模块.正则表达式模式被编译成一系列的字节码,然后由用C编写的匹配引擎执行. #正则表达式: ...
- python异步IO编程(二)
python异步IO编程(二) 目录 开门见山 Async IO设计模式 事件循环 asyncio 中的其他顶层函数 开门见山 下面我们用两个简单的例子来让你对异步IO有所了解 import asyn ...
- css进阶之二:flex弹性布局
布局模式是指一个盒子与其兄弟.祖先盒的关系决定其尺寸与位置的算法.css2.1中定义了四种布局模式,分别是块布局.行内布局.表格布局.以及定位布局.css3引入了新的布局模式Flexbox布局,灵活度 ...
随机推荐
- 精通Spring 4.x 企业应用开发实战- 1.6 新特性
- layui使用OSS上传
1.首先要把aliyun-oss-sdk.js包下载下来,放到指定的目录下面 在要用的页面引入或者在index.html入口文件全局引入: <script src="util/ali ...
- uglifyjs-webpack-plugin配置
项目使用vuecli3搭建,在vue.config.js文件中进行配置,主要配置了去除线上环境的打印信息. 首先安装插件, 执行命令 npm install uglifyjs-webpack-plug ...
- rename基本操作
电脑是Macbook, 用Homebrew先安装rename. 如果没安装Homebrew 直接复制到terminal中回车, 时间稍长. ruby -e "$(curl -fsSL htt ...
- spring-mvc.xml
<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.spr ...
- python list dict util (分割,分组)
1.list数据分割为多个小列表 (java lists.partition) 2. 分组 import itertools def partition(mylist, size): " ...
- modbus通讯协议详解
1.Modbus 协议简介 Modbus协议是一种已广泛应用于当今工业控制领域的通用通讯协议.通过此协议,控制器相互之间.或控制器经由网络(如以太网)可以和其它设备之间进行通信.Modbus协议使用 ...
- 如何建设私有云原生 Serverless 平台
随着云计算的普及,越来越多的企业开始将业务应用迁移到云上.然而,如何构建一套完整的云原生 Serverless 平台,依然是一个需要考虑的问题. Serverless的发展趋势 云计算行业从 IaaS ...
- Opengl ES之矩阵变换(上)
前言 说到矩阵变换,我们第一时间想到的就是大学时代的线性代数这些复杂的东西,突然有了一种令人从入门到放弃的念头,不慌,作为了一个应用层的CV工程师, 在实际应用中线性代数哪些复杂的计算根本不用我们自己 ...
- Java:数据表的字段设计了默认值0不生效的原因
在数据表里给字段设置了默认值为0,但是在插入的时候不生效,数据表设计如下 通过数据表生成的实体类 查看代码 @Data @TableName(value = "user") @No ...