先看段代码:

1 for (int i = 0; i < 10; i++)
2 {
3 Task.Factory.StartNew(()=>Console.WriteLine($"{Thread.CurrentThread.ManagedThreadId} ~ {i}"));
4 }

从代码上可以看出我们预期是打印1~10,但实际的打印结果是:

 1 7 ~ 10
2 4 ~ 10
3 10 ~ 10
4 9 ~ 10
5 4 ~ 10
6 3 ~ 10
7 5 ~ 10
8 9 ~ 10
9 6 ~ 10
10 8 ~ 10

与预期的不一致,我们预期是打印数字1到10,但实际打印出来的是10次10。因为这几个lambda表达式中使用了同一个变量,并且这些匿名函数共享变量值。

再来看下面这段代码:

1 Action<int> displayNumber = n => Console.WriteLine(n);
2 int i = 5;
3 Task taskOne = Task.Factory.StartNew(() => displayNumber(i));
4 i = 7;
5 Task taskTwo = Task.Factory.StartNew(() => displayNumber(i));
6 Task.WaitAll(taskOne,taskTwo);

输出结果:

7
7

当闭包通过lambda表达式捕获可变变量时,lambda捕获变量的引用,而不是捕获该变量的当前值。因此,如果任务在变量的引用值更改后运行,则该值将是内存中最新的值,而不是捕获变量时的值。

为解决该问题,我们引入Parallel类来解决问题:

1 Parallel.For(0,10,i=>Console.WriteLine($"{Thread.CurrentThread.ManagedThreadId} ~ {i}"));

打印结果:

 1 1 ~ 0
2 1 ~ 2
3 3 ~ 1
4 3 ~ 4
5 3 ~ 7
6 3 ~ 8
7 3 ~ 9
8 1 ~ 3
9 5 ~ 5
10 4 ~ 6

Parallel 类 提供对并行循环和区域的支持, 现在我们看下Parallel.for的代码:

  1 // this needs to be in try-block because it can throw in  BuggyScheduler.MaxConcurrencyLevel
2 rootTask = new ParallelForReplicatingTask(
3 parallelOptions,
4 delegate
5 {
6 //
7 // first thing we do upon enterying the task is to register as a new "RangeWorker" with the
8 // shared RangeManager instance.
9 //
10 // If this call returns a RangeWorker struct which wraps the state needed by this task
11 //
12 // We need to call FindNewWork32() on it to see whether there's a chunk available.
13 //
14 // Cache some information about the current task
15 Task currentWorkerTask = Task.InternalCurrent;
16 bool bIsRootTask = (currentWorkerTask == rootTask);
17 RangeWorker currentWorker = new RangeWorker();
18 Object savedStateFromPreviousReplica = currentWorkerTask.SavedStateFromPreviousReplica;
19 if (savedStateFromPreviousReplica is RangeWorker)
20 currentWorker = (RangeWorker)savedStateFromPreviousReplica;
21 else
22 currentWorker = rangeManager.RegisterNewWorker();
23 // These are the local index values to be used in the sequential loop.
24 // Their values filled in by FindNewWork32
25 int nFromInclusiveLocal;
26 int nToExclusiveLocal;
27 if (currentWorker.FindNewWork32(out nFromInclusiveLocal, out nToExclusiveLocal) == false ||
28 sharedPStateFlags.ShouldExitLoop(nFromInclusiveLocal) == true)
29 {
30 return; // no need to run
31 }
32 // ETW event for ParallelFor Worker Fork
33 if (TplEtwProvider.Log.IsEnabled())
34 {
35 TplEtwProvider.Log.ParallelFork((currentWorkerTask != null ? currentWorkerTask.m_taskScheduler.Id : TaskScheduler.Current.Id), (currentWorkerTask != null ? currentWorkerTask.Id : 0),
36 forkJoinContextID);
37 }
38 TLocal localValue = default(TLocal);
39 bool bLocalValueInitialized = false; // Tracks whether localInit ran without exceptions, so that we can skip localFinally if it wasn't
40 try
41 {
42 // Create a new state object that references the shared "stopped" and "exceptional" flags
43 // If needed, it will contain a new instance of thread-local state by invoking the selector.
44 ParallelLoopState32 state = null;
45 if (bodyWithState != null)
46 {
47 Contract.Assert(sharedPStateFlags != null);
48 state = new ParallelLoopState32(sharedPStateFlags);
49 }
50 else if (bodyWithLocal != null)
51 {
52 Contract.Assert(sharedPStateFlags != null);
53 state = new ParallelLoopState32(sharedPStateFlags);
54 if (localInit != null)
55 {
56 localValue = localInit();
57 bLocalValueInitialized = true;
58 }
59 }
60 // initialize a loop timer which will help us decide whether we should exit early
61 LoopTimer loopTimer = new LoopTimer(rootTask.ActiveChildCount);
62 // Now perform the loop itself.
63 do
64 {
65 if (body != null)
66 {
67 for (int j = nFromInclusiveLocal;
68 j < nToExclusiveLocal && (sharedPStateFlags.LoopStateFlags == ParallelLoopStateFlags.PLS_NONE // fast path check as SEL() doesn't inline
69 || !sharedPStateFlags.ShouldExitLoop()); // the no-arg version is used since we have no state
70 j += 1)
71 {
72 body(j);
73 }
74 }
75 else if (bodyWithState != null)
76 {
77 for (int j = nFromInclusiveLocal;
78 j < nToExclusiveLocal && (sharedPStateFlags.LoopStateFlags == ParallelLoopStateFlags.PLS_NONE // fast path check as SEL() doesn't inline
79 || !sharedPStateFlags.ShouldExitLoop(j));
80 j += 1)
81 {
82 state.CurrentIteration = j;
83 bodyWithState(j, state);
84 }
85 }
86 else
87 {
88 for (int j = nFromInclusiveLocal;
89 j < nToExclusiveLocal && (sharedPStateFlags.LoopStateFlags == ParallelLoopStateFlags.PLS_NONE // fast path check as SEL() doesn't inline
90 || !sharedPStateFlags.ShouldExitLoop(j));
91 j += 1)
92 {
93 state.CurrentIteration = j;
94 localValue = bodyWithLocal(j, state, localValue);
95 }
96 }
97 // Cooperative multitasking hack for AppDomain fairness.
98 // Check if allowed loop time is exceeded, if so save current state and return. The self replicating task logic
99 // will detect this, and queue up a replacement task. Note that we don't do this on the root task.
100 if (!bIsRootTask && loopTimer.LimitExceeded())
101 {
102 currentWorkerTask.SavedStateForNextReplica = (object)currentWorker;
103 break;
104 }
105 }
106 // Exit if we can't find new work, or if the loop was stoppped.
107 while (currentWorker.FindNewWork32(out nFromInclusiveLocal, out nToExclusiveLocal) &&
108 ((sharedPStateFlags.LoopStateFlags == ParallelLoopStateFlags.PLS_NONE) ||
109 !sharedPStateFlags.ShouldExitLoop(nFromInclusiveLocal)));
110 }
111 catch
112 {
113 // if we catch an exception in a worker, we signal the other workers to exit the loop, and we rethrow
114 sharedPStateFlags.SetExceptional();
115 throw;
116 }
117 finally
118 {
119 // If a cleanup function was specified, call it. Otherwise, if the type is
120 // IDisposable, we will invoke Dispose on behalf of the user.
121 if (localFinally != null && bLocalValueInitialized)
122 {
123 localFinally(localValue);
124 }
125 // ETW event for ParallelFor Worker Join
126 if (TplEtwProvider.Log.IsEnabled())
127 {
128 TplEtwProvider.Log.ParallelJoin((currentWorkerTask != null ? currentWorkerTask.m_taskScheduler.Id : TaskScheduler.Current.Id), (currentWorkerTask != null ? currentWorkerTask.Id : 0),
129 forkJoinContextID);
130 }
131 }
132 },
133 creationOptions, internalOptions);
134 rootTask.RunSynchronously(parallelOptions.EffectiveTaskScheduler); // might throw TSE
135 rootTask.Wait();
136 // If we made a cancellation registration, we need to clean it up now before observing the OCE
137 // Otherwise we could be caught in the middle of a callback, and observe PLS_STOPPED, but oce = null
138 if (parallelOptions.CancellationToken.CanBeCanceled)
139 {
140 ctr.Dispose();
141 }
142 // If we got through that with no exceptions, and we were canceled, then
143 // throw our cancellation exception
144 if (oce != null) throw oce;

body对于迭代范围 (的每个值调用一次委托 fromInclusive , toExclusive) 。提供两个参数:

1、一个 Int32 值,该值表示迭代次数。

2、ParallelLoopState可用于提前中断循环的实例。ParallelLoopState对象是由编译器创建的; 它不能在用户代码中实例化。

继续来看:

Parallel.For(0, 10, (i,state) =>
{
if (i > 5)
state.Break();
Console.WriteLine($"{Thread.CurrentThread.ManagedThreadId} ~ {i}");
} );

输出:

1 1 ~ 0
2 1 ~ 1
3 1 ~ 2
4 1 ~ 3
5 1 ~ 4
6 1 ~ 5
7 1 ~ 6

在上面的方法中我们使用了 break方法。

调用 Break 方法会通知 for 操作,在当前的迭代之后,无需执行迭代。不过,如果所有迭代尚未执行,则仍必须执行当前的所有迭代。

因此,调用 Break 类似于 for c# 等语言中的传统循环内的中断操作,但它并不是完美的替代方法:例如,无法保证当前的迭代不会执行。

今天就先写道这里~


多线程那点事—Parallel.for的更多相关文章

  1. 关于JAVA多线程的那些事__初心者

    前言 其实事情的经过也许会复杂了点,这事还得从两个月前开始说.那天,我果断不干IT支援.那天,我立志要做一个真正的程序猿.那天,我26岁11个月.那天,我开始看Android.那天,我一边叨念着有朋自 ...

  2. 【QT】 Qt多线程的“那些事”

    目录 一.前言 二.QThread源码浅析 2.1 QThread类的定义源码 2.2 QThread::start()源码 2.3 QThreadPrivate::start()源码 2.4 QTh ...

  3. 【Python爬虫实战】多线程爬虫---糗事百科段子爬取

    多线程爬虫:即程序中的某些程序段并行执行,合理地设置多线程,可以让爬虫效率更高糗事百科段子普通爬虫和多线程爬虫分析该网址链接得出:https://www.qiushibaike.com/8hr/pag ...

  4. java多线程那点事

    屌丝程序员们对自己的技术能力总是毫不掩饰的高调,更有甚者每当完成一个简单的功能或算法实现,恨不得从工位上跳起来,生怕谁不知道一样,心情能理解,但个人完全鄙视这种行为.说到底,大家日常的coding,大 ...

  5. linux 多线程那点事

    说明:对多线程与相互排斥锁不熟悉的请參考其他 #include <pthread.h> #include <stdio.h> #include <stdlib.h> ...

  6. Java多线程编程那些事:volatile解惑--转

    http://www.infoq.com/cn/articles/java-multi-thread-volatile/ 1. 前言 volatile关键字可能是Java开发人员“熟悉而又陌生”的一个 ...

  7. C#多线程那点事——信号量(Semaphore)

    信号量说简单点就是为了线程同步,或者说是为了限制线程能运行的数量. 那它又是怎么限制线程的数量的哩?是因为它内部有个计数器,比如你想限制最多5个线程运行,那么这个计数器的值就会被设置成5,如果一个线程 ...

  8. .Net进阶系列(10)-异步多线程综述(被替换)

    一. 综述 经过两个多个周的整理,异步多线程章节终于整理完成,如下图所示,主要从基本概念.委托的异步调用.Thread多线程.ThreadPool多线程.Task.Parallel并行计算.async ...

  9. OpenMP多线程linux下的使用,简单化

    http://hi.baidu.com/diwulechao/item/bc6d865c411b813c32e0a932 http://www.cnblogs.com/yangyangcv/archi ...

随机推荐

  1. buuctf-web-[极客大挑战 2019]BuyFlag 1

    打开网页,然后发现menu中有个buyflag的连接,点进去 如果你想买这个flag ,你必须是来自CUIT的一名学生,还必须回答正确的密码.简单了解,我们查看源码,发现思路 POST方式传入两个参数 ...

  2. 大数据开发——Hive笔记

    写在前面 hive是基于Hadoop的一个数据仓库工具,可以将结构化的数据文件映射为一张数据库表,并提供简单的sql查询功能,可以将sql语句转换为MapReduce任务进行运行.Hive的运行原理- ...

  3. PyQt(Python+Qt)学习随笔:QListWidget对项进行排序的sortItems方法

    老猿Python博文目录 专栏:使用PyQt开发图形界面Python应用 老猿Python博客地址 QListWidget的sortItems方法用于对列表部件中所有项按参数进行排序,相关调用语法如下 ...

  4. PyQt(Python+Qt)学习随笔:QListView的resizeMode属性

    老猿Python博文目录 专栏:使用PyQt开发图形界面Python应用 老猿Python博客地址 QListView的resizeMode属性用于控制调整视图大小时是否再次排列视图中的数据项,其类型 ...

  5. PyQt学习随笔:ListView控件的视图和数据模型分离案例

    Qt 中view类控件的目的是实现数据和模型分离,控件展示数据,数据保存在数据存储中,数据存储中的数据改变了,则控件中展示的数据跟随改变.当设计时只指定了一个控件和一个数据存储关联时,这种分离虽然也能 ...

  6. PyQt(Python+Qt)学习随笔:Qt Designer中部件的三个属性sizeHint缺省尺寸、minimumSizeHint建议最小尺寸和minimumSize最小尺寸

    在Qt Designer中的每个部件,要调整部件大小,需要关注三个部件大小相关的属性:sizeHint.minimumSizeHint.minimumSize: 1.sizeHint:为布局管理器中部 ...

  7. Java基础学习之面向对象(4)

    目录 1.面向对象概述 1.1.类与对象的关系 1.2.类的具体描述 2.面向对象的三大特性 2.1.继承 2.2.多态 2.3.封装 1.面向对象概述 1.1.类与对象的关系 有对象吗,没有的话我给 ...

  8. springboot:异步调用@Async

    在后端开发中经常遇到一些耗时或者第三方系统调用的情况,我们知道Java程序一般的执行流程是顺序执行(不考虑多线程并发的情况),但是顺序执行的效率肯定是无法达到我们的预期的,这时就期望可以并行执行,常规 ...

  9. Jmeter(10)逻辑控制器

    逻辑控制器可以控制采样器的执行顺序,所以控制器需要和采样器一起使用 Jmeter中的逻辑控制器分为两类 1.控制测试计划执行过程中节点的逻辑顺序,如循环控制器.If控制器 2.对测试计划中的脚本进行分 ...

  10. JavaSE17-File&递归&字节流

    1.File类 1.1 File类概述和构造方法 File类介绍 它是文件和目录路径名的抽象表示 文件和目录是可以通过File封装成对象的 对于File而言,其封装的并不是一个真正存在的文件,仅仅是一 ...