C# 使用Parallelmatlab并行计算实例开发Parallel.For、Parallel.Foreach实例

您的位置: >>
现在已经进入了多核的时代,我们的程序如何更多的利用好cpu,答案是并行处理。在.net4.0之前我们要开发并行的程序是非常的困难,在.net4.0中,在命名空间System.Threading.Tasks提供了方便的并行开发的类库。本文中主要看看Data Parallel,看看并行的For、Foreach。
  Parallel.For
  首先先写一个普通的循环:
private void NormalFor(){
for (var i = 0; i & 10000; i++)
for (var j = 0; j & 1000; j++)
for (var k = 0; k & 100; k++)
DoSomething();
  再看一个并行的For语句:
private void ParallelFor(){
Parallel.For(0, 10000, i =&
for (int j = 0; j & 1000; j++)
for (var k = 0; k & 100; k++)
DoSomething();
  看下测试方法:
[TestMethod()]public void TestForLoop() {
_StopWatch.Start();
this.NormalFor();
_StopWatch.Stop();
Console.WriteLine("NormalForLoop Runned Time:{0}", _StopWatch.ElapsedMilliseconds);
_StopWatch.Reset();
_StopWatch.Start();
this.ParallelFor();
_StopWatch.Stop();
Console.WriteLine("Parallel Loop:{0}", _StopWatch.ElapsedMilliseconds);}
  测试结果:
  上面的例子中,只是将最外层的For语句替换成了Parallel.For,我们可以看到Parallel执行速度提高了近一倍。下面我把里面的循环也改成并行的:
private void ParallelNestedFor(){
Parallel.For(0, 10000, i =&
Parallel.For(0, 1000, j =&
for (var k = 0; k & 100; k++)
DoSomething();
  结果:
  也许会令我们感到惊讶的是:嵌套Paralled For之后速度并没有更快,反而稍微慢了。其实是这样的,因为我们的示例中大部分操作是在最外层循环,而在并行操作中会需要缓存数据等会浪费一定的性能。当我们把最外层的循环调整成100,中间层为10000时,我们来看下结果:
  所以,是否需要嵌套的时候,需要我们根据一些实际情况来决定,不过对于大部分操作,最外层的并行处理已经足够了。
  Parallel.ForEach
  我们来看两段很简单的代码:
private void NormalForeach() {
foreach (var file in GetFiles())
DoSomething();
}private void ParallelForeach(){
Parallel.ForEach(GetFiles(), file =& {
DoSomething();
  测试的结果:
  Foreach的使用跟For使用几乎是差不多了,只是在对非泛型的Collection进行操作的时候,需要通过Cast方法进行转换。
  在本文中,我们简单的介绍了Parallel.For跟Parallel.Foreach方法的使用,感受了下并行编程给我们带来的速度上的优势,在下篇文章中会介绍如何跳出循环以及一些异常的处理。
.NET技术热门文章
.NET技术最新文章C#并行编程中的Parallel.Invoke
来源:博客园
一、基础知识
并行编程:并行编程是指软件开发的代码,它能在同一时间执行多个计算任务,提高执行效率和性能一种编程方式,属于多线程编程范畴。所以我们在设计过程中一般会将很多任务划分成若干个互相独立子任务,这些任务不考虑互相的依赖和顺序。这样我们就可以使用很好的使用并行编程。但是我们都知道多核处理器的并行设计使用共享内存,如果没有考虑并发问题,就会有很多异常和达不到我们预期的效果。不过还好NET Framework4.0引入了Task Parallel Library(TPL)实现了基于任务设计而不用处理重复复杂的线程的并行开发框架。它支持数据并行,任务并行与流水线。核心主要是Task,但是一般简单的并行我们可以利用Parallel提供的静态类如下三个方法。
Parallel.Invoke
对给定任务实现并行开发
Parallel.For
对固定数目的任务提供循环迭代并行开发
parallel.Foreach 对固定数目的任务提供循环迭代并行开发
注意:所有的并行开发不是简单的以为只要将For或者Foreach换成Parallel.For与Parallel.Foreach这样简单。
PS:从简单的Invoke开始逐步深入探讨并行开发的主要知识点,也对自己学习过程中的积累做个总结,其中参考了博客园中的其他优秀博文
首先感谢您,在我学习并行开发过程中,您的博文对我帮助很大。
二、Parallel.Invoke在并行中的使用
首先我们来看看它的两个重载方法:


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

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

 
Invoke主要接受params的委托actions,比如我们要同时执行三个任务,我们可以这样利用


Parallel.Invoke(() =& Task1(), () =& Task2(), () =& Task3());
Parallel.Invoke(Task1, Task2, Task3);
Parallel.Invoke(
delegate () { Task3(); console.write('do someting!');});

这样Invoke就简单实现了Task1,Task2,Task3的并行开发。下面我们用实例来说明他们的执行规则。以及两个重载方法的使用。
三 、Demo 
1、 Demo 1:

public class ParallelInvoke
/// &summary&
/// Invoke方式一 action
/// &/summary&
void Client1()
Stopwatch stopWatch = new Stopwatch();
Console.WriteLine("主线程:{0}线程ID : {1};开始", "Client1", Thread.CurrentThread.ManagedThreadId);
stopWatch.Start();
Parallel.Invoke(() =& Task1("task1"), () =& Task2("task2"), () =& Task3("task3"));
stopWatch.Stop();
Console.WriteLine("主线程:{0}线程ID : {1};结束,共用时{2}ms", "Client1", Thread.CurrentThread.ManagedThreadId, stopWatch.ElapsedMilliseconds);
}

private void Task1(string data)
Thread.Sleep(5000);
Console.WriteLine("任务名:{0}线程ID : {1}", data, Thread.CurrentThread.ManagedThreadId);
}

private void Task2(string data)
Console.WriteLine("任务名:{0}线程ID : {1}", data, Thread.CurrentThread.ManagedThreadId);
}

private void Task3(string data)
Console.WriteLine("任务名:{0}线程ID : {1}", data, Thread.CurrentThread.ManagedThreadId);
}
}

执行运行后结果:

我们看到Invoke 执行Task三个方法主要有以下几个特点:
1、没有固定的顺序,每个Task可能是不同的线程去执行,也可能是相同的;
2、主线程必须等Invoke中的所有方法执行完成后返回才继续向下执行;这样对我们以后设计并行的时候,要考虑每个Task任务尽可能差不多,如果相差很大,比如一个时间非常长,其他都比较短,这样一个线程可能会影响整个任务的性能。这点非常重要
3、这个非常简单就实现了并行,不用我们考虑线程问题。主要Framework已经为我们控制好线程池的问题。
ps:如果其中有一个异常怎么办? 带做这个问题修改了增加了一个Task4.

 

public class ParallelInvoke
/// &summary&
/// Invoke方式一 action
/// &/summary&
void Client1()
Stopwatch stopWatch = new Stopwatch();
Console.WriteLine("主线程:{0}线程ID : {1};开始", "Client1", Thread.CurrentThread.ManagedThreadId);
stopWatch.Start();

Parallel.Invoke(() =& Task1("task1"), () =& Task2("task2"), () =& Task3("task3"), delegate () { throw new Exception("我这里发送了异常"); });
catch (AggregateException ae)
foreach (var ex in ae.InnerExceptions)
Console.WriteLine(ex.Message);
stopWatch.Stop();
Console.WriteLine("主线程:{0}线程ID : {1};结束,共用时{2}ms", "Client1", Thread.CurrentThread.ManagedThreadId, stopWatch.ElapsedMilliseconds);
}

 
 
 
主要看 delegate() { throw new Exception("我这里发送了异常");} 增加了这个委托Task3. 然后我们看结果:

这里我们发现即使有异常程序也会完成执行,而且不会影响其他Task的执行。
3、demo3 重载方法ParallelOptions 的使用。
理解ParallelOptions建议大家讲的非常详细。
主要理解两个参数:
CancellationToken
控制线程的取消
MaxDegreeOfParallelism
设置最大的线程数,有时候可能会跑遍所有的内核,为了提高其他应用程序的稳定性,就要限制参与的内核
下面从代码上看效果如何?

 public class ParallelInvoke
{
     // 定义CancellationTokenSource 控制取消
readonly CancellationTokenSource _cts = new CancellationTokenSource();

/// &summary&
/// Invoke方式一 action
/// &/summary&
void Client1()
Console.WriteLine("主线程:{0}线程ID : {1};开始{2}", "Client3", Thread.CurrentThread.ManagedThreadId, DateTime.Now);
var po = new ParallelOptions
CancellationToken = _cts.Token, // 控制线程取消
MaxDegreeOfParallelism = 3
// 设置最大的线程数3,仔细观察线程ID变化
Parallel.Invoke(po, () =& Task1("task1"), ()=&Task5(po), Task6);
Console.WriteLine("主线程:{0}线程ID : {1};结束{2}", "Client3", Thread.CurrentThread.ManagedThreadId, DateTime.Now);
}

private void Task1(string data)
Thread.Sleep(5000);
Console.WriteLine("任务名:{0}线程ID : {1}", data, Thread.CurrentThread.ManagedThreadId);
// 打印数字
private void Task5(ParallelOptions po)
Console.WriteLine("进入Task5线程ID : {0}", Thread.CurrentThread.ManagedThreadId);
int i = 0;
while (i & 100)
// 判断是否已经取消
if (po.CancellationToken.IsCancellationRequested)
Console.WriteLine("已经被取消。");
return;
}

Thread.Sleep(100);
Console.Write(i + " ");
Interlocked.Increment(ref i);
}

}

/// &summary&
/// 10秒后取消
/// &/summary&
private void Task6()
Console.WriteLine("进入取消任务,Task6线程ID : {0}", Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(1000 * 10);
_cts.Cancel();
Console.WriteLine("发起取消请求...........");


执行结果:

从程序结果我们看到以下特点:
1、程序在执行过程中线程数码不超过3个。
2、CancellationTokenSource/CancellationToken控制任务的取消。
四、总结
Parallel.Invoke 的使用过程中我们要注意以下特点: 
1、没有特定的顺序,Invoke中的方法全部执行完才返回,但是即使有异常在执行过程中也同样会完成,他只是一个很简单的并行处理方法,特点就是简单,不需要我们考虑线程的问题。
2、如果在设计Invoke中有个需要很长时间,这样会影响整个Invoke的效率和性能,这个我们在设计每个task时候必须去考虑的。
3、Invoke 参数是委托方法。
4、当然Invoke在每次调用都有开销的,不一定并行一定比串行好,要根据实际情况,内核环境多次测试调优才可以。
5、异常处理比较复杂。
免责声明:本站部分内容、图片、文字、视频等来自于互联网,仅供大家学习与交流。相关内容如涉嫌侵犯您的知识产权或其他合法权益,请向本站发送有效通知,我们会及时处理。反馈邮箱&&&&。
学生服务号
在线咨询,奖学金返现,名师点评,等你来互动C#并行编程中的Parallel.Invoke
时间: 13:09:55
&&&& 阅读:85
&&&& 评论:
&&&& 收藏:0
标签:一、基础知识
&&&& 并行编程:并行编程是指软件开发的代码,它能在同一时间执行多个计算任务,提高执行效率和性能一种编程方式,属于多线程编程范畴。所以我们在设计过程中一般会将很多任务划分成若干个互相独立子任务,这些任务不考虑互相的依赖和顺序。这样我们就可以使用很好的使用并行编程。但是我们都知道多核处理器的并行设计使用共享内存,如果没有考虑并发问题,就会有很多异常和达不到我们预期的效果。不过还好NET Framework4.0引入了Task Parallel Library(TPL)实现了基于任务设计而不用处理重复复杂的线程的并行开发框架。它支持数据并行,任务并行与流水线。核心主要是Task,但是一般简单的并行我们可以利用Parallel提供的静态类如下三个方法。
&&&&&&&& Parallel.Invoke& 对给定任务实现并行开发
&&&&&&&& Parallel.For& 对固定数目的任务提供循环迭代并行开发
&&&&&&&& parallel.Foreach 对固定数目的任务提供循环迭代并行开发
注意:所有的并行开发不是简单的以为只要将For或者Foreach换成Parallel.For与Parallel.Foreach这样简单。
PS:从简单的Invoke开始逐步深入探讨并行开发的主要知识点,也对自己学习过程中的积累做个总结,其中参考了博客园中的其他优秀博文
&&&&& 首先感谢您,在我学习并行开发过程中,您的博文对我帮助很大。
二、Parallel.Invoke在并行中的使用
&&&&& 首先我们来看看它的两个重载方法:&&&
  public static void Invoke(params Action[] actions);
  public static void Invoke(ParallelOptions parallelOptions, params Action[] actions);
&&&&&& Invoke主要接受params的委托actions,比如我们要同时执行三个任务,我们可以这样利用 &&&
Parallel.Invoke(() =& Task1(), () =& Task2(), () =& Task3());
Parallel.Invoke(Task1, Task2, Task3);
Parallel.Invoke(&&&&&&&&&&&&&&& () =&&&&&&&&&&&&&&&& {&&&&&&&&&&&&&&&&&&& Task1();&&&&&&&&&&&&&&& },&&&&&&&&&&&&&&
Task2,&&&&&&&&&&&& && delegate () { Task3(); console.write(‘do someting!‘);});
这样Invoke就简单实现了Task1,Task2,Task3的并行开发。下面我们用实例来说明他们的执行规则。以及两个重载方法的使用。
&&&& 1、 Demo 1:
public class ParallelInvoke
/// &summary&
/// Invoke方式一 action
/// &/summary&
void Client1()
Stopwatch stopWatch = new Stopwatch();
Console.WriteLine("主线程:{0}线程ID : {1};开始", "Client1", Thread.CurrentThread.ManagedThreadId);
stopWatch.Start();
Parallel.Invoke(() =& Task1("task1"), () =& Task2("task2"), () =& Task3("task3"));
stopWatch.Stop();
Console.WriteLine("主线程:{0}线程ID : {1};结束,共用时{2}ms", "Client1", Thread.CurrentThread.ManagedThreadId, stopWatch.ElapsedMilliseconds);
private void Task1(string data)
Thread.Sleep(5000);
Console.WriteLine("任务名:{0}线程ID : {1}", data, Thread.CurrentThread.ManagedThreadId);
private void Task2(string data)
Console.WriteLine("任务名:{0}线程ID : {1}", data, Thread.CurrentThread.ManagedThreadId);
private void Task3(string data)
Console.WriteLine("任务名:{0}线程ID : {1}", data, Thread.CurrentThread.ManagedThreadId);
执行运行后结果:
我们看到Invoke 执行Task三个方法主要有以下几个特点:
&&&&& 1、没有固定的顺序,每个Task可能是不同的线程去执行,也可能是相同的;
&&&&& 2、主线程必须等Invoke中的所有方法执行完成后返回才继续向下执行;这样对我们以后设计并行的时候,要考虑每个Task任务尽可能差不多,如果相差很大,比如一个时间非常长,其他都比较短,这样一个线程可能会影响整个任务的性能。这点非常重要
&&&&& 3、这个非常简单就实现了并行,不用我们考虑线程问题。主要Framework已经为我们控制好线程池的问题。
ps:如果其中有一个异常怎么办? 带做这个问题修改了增加了一个Task4.
&&& 2、 Demo2&
public class ParallelInvoke
/// &summary&
/// Invoke方式一 action
/// &/summary&
void Client1()
Stopwatch stopWatch = new Stopwatch();
Console.WriteLine("主线程:{0}线程ID : {1};开始", "Client1", Thread.CurrentThread.ManagedThreadId);
stopWatch.Start();
Parallel.Invoke(() =& Task1("task1"), () =& Task2("task2"), () =& Task3("task3"), delegate () { throw new Exception("我这里发送了异常"); });
catch (AggregateException ae)
foreach (var ex in ae.InnerExceptions)
Console.WriteLine(ex.Message);
stopWatch.Stop();
Console.WriteLine("主线程:{0}线程ID : {1};结束,共用时{2}ms", "Client1", Thread.CurrentThread.ManagedThreadId, stopWatch.ElapsedMilliseconds);
主要看 delegate() { throw new Exception("我这里发送了异常");} 增加了这个委托Task3. 然后我们看结果:
&&& 这里我们发现即使有异常程序也会完成执行,而且不会影响其他Task的执行。
&&& 3、demo3 重载方法ParallelOptions 的使用。
&& 理解ParallelOptions建议大家讲的非常详细。
&& 主要理解两个参数:
&& & CancellationToken&&&& 控制线程的取消&&&& MaxDegreeOfParallelism& 设置最大的线程数,有时候可能会跑遍所有的内核,为了提高其他应用程序的稳定性,就要限制参与的内核
&&&& 下面从代码上看效果如何?
public class ParallelInvoke
    // 定义CancellationTokenSource 控制取消
readonly CancellationTokenSource _cts = new CancellationTokenSource();
/// &summary&
/// Invoke方式一 action
/// &/summary&
void Client1()
Console.WriteLine("主线程:{0}线程ID : {1};开始{2}", "Client3", Thread.CurrentThread.ManagedThreadId, DateTime.Now);
var po = new ParallelOptions
CancellationToken = _cts.Token, // 控制线程取消
MaxDegreeOfParallelism = 3
// 设置最大的线程数3,仔细观察线程ID变化
Parallel.Invoke(po, () =& Task1("task1"), ()=&Task5(po), Task6);
Console.WriteLine("主线程:{0}线程ID : {1};结束{2}", "Client3", Thread.CurrentThread.ManagedThreadId, DateTime.Now);
private void Task1(string data)
Thread.Sleep(5000);
Console.WriteLine("任务名:{0}线程ID : {1}", data, Thread.CurrentThread.ManagedThreadId);
// 打印数字
private void Task5(ParallelOptions po)
Console.WriteLine("进入Task5线程ID : {0}", Thread.CurrentThread.ManagedThreadId);
int i = 0;
while (i & 100)
// 判断是否已经取消
if (po.CancellationToken.IsCancellationRequested)
Console.WriteLine("已经被取消。");
Thread.Sleep(100);
Console.Write(i + " ");
Interlocked.Increment(ref i);
/// &summary&
/// 10秒后取消
/// &/summary&
private void Task6()
Console.WriteLine("进入取消任务,Task6线程ID : {0}", Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(1000 * 10);
_cts.Cancel();
Console.WriteLine("发起取消请求...........");
执行结果:
&&&& 从程序结果我们看到以下特点:
&&&&&&&&&&& 1、程序在执行过程中线程数码不超过3个。
&&&&&&&&&&& 2、CancellationTokenSource/CancellationToken控制任务的取消。
& & & Parallel.Invoke 的使用过程中我们要注意以下特点:
&&&&& 1、没有特定的顺序,Invoke中的方法全部执行完才返回,但是即使有异常在执行过程中也同样会完成,他只是一个很简单的并行处理方法,特点就是简单,不需要我们考虑线程的问题。
&&&&& 2、如果在设计Invoke中有个需要很长时间,这样会影响整个Invoke的效率和性能,这个我们在设计每个task时候必须去考虑的。
&&&&& 3、Invoke 参数是委托方法。
&&&&& 4、当然Invoke在每次调用都有开销的,不一定并行一定比串行好,要根据实际情况,内核环境多次测试调优才可以。
&&&&& 5、异常处理比较复杂。
&&&&标签:
&&国之画&&&& &&&&chrome插件
版权所有 京ICP备号-2
迷上了代码!一隐归尘,浮志潺潺
任务:几千条(大量)数据往服务器数据库填写。要求单开线程执行,分割成小数据包,多线程运行。
实现方法:Parallel与TaskFactory都可以。
主要代码:
int _maxLength = 20, _maxChannel = 2;//同时最多2条通道,每条通道最多20个数据
bool _isCancel = false;
private void btnWrite_Click(object sender, EventArgs e)
var tmpEmails = _emails.Where(x =& !x.Value).Select(x =& x.Key).ToList();
var state = 0;
_isCancel = false;
SetControlEnable(false);
lblProgress.Text = "* 已完成 0%";
var channels = (tmpEmails.Count / _maxLength) + ((tmpEmails.Count % _maxLength & 0) ? 1 : 0);//总共多少条通道
var times = (channels / _maxChannel) + ((channels % _maxChannel & 0) ? 1 : 0);//单服务器分多次
new Action(() =&
for (int j = 0; j & j++)
if (_isCancel)
MessageBox.Show("任务取消!");
var currChannel = Math.Min(_maxChannel, (channels - j * _maxChannel));//两者取其小的
_bar = new Barrier(currChannel);//根据次数设置栅栏
var tasks = new Action[currChannel];
for (int i = 0; i & currC i++)
var subData = tmpEmails.Skip((i + j * _maxChannel) * _maxLength).Take(_maxLength).ToList();
tasks[i] = () =&
if (_isCancel) return;
var resMsg = 0;
Connect2WCF.RunSync(sc =& resMsg = sc.UpdateMailState(subData, state));
if (resMsg == -1)
MessageBox.Show("保存失败了?详情可以查数据库日志表");
else if (resMsg == 0)
subData.ForEach(one =& _emails[one] = true);//标记已经完成的。
new Action(() =& txtEmails.Text = string.Join("\r\n", _emails.Where(x =& !x.Value).Select(x =& x.Key))).InvokeRun(this);
_bar.SignalAndWait();
Parallel.Invoke(tasks);
new Action(() =& lblProgress.Text = "* 已完成 " + ((100 * (j + 1) / times)) + "%").InvokeRun(this);
new Action(() =& SetControlEnable(true)).InvokeRun(this);
}).RunThread();
用Barrier和Parallel.Invoke结合来实现分割小数据包,每次用两个线程,每个线程传递20条数据,两个线程的数据都完成后,刷新完成的进度。isCancel作为取消操作的开关。实现的效果较下面的TaskFactory好。
TaskFactory:
CancellationTokenSource cts = new CancellationTokenSource();
int maxLength = 20, maxChannel = 2;//同时最多2条通道,每条通道最多20个数据
private void btnWrite_Click(object sender, EventArgs e)
cts = new CancellationTokenSource();
var tmpEmails = _emails.Where(x =& !x.Value).Select(x =& x.Key).ToList();
var state = 0;
SetControlEnable(false);
lblProgress.Text = "* 已完成 0%";
var channels = (tmpEmails.Count / maxLength) + ((tmpEmails.Count % maxLength & 0) ? 1 : 0);//总共多少条通道
var times = (channels / maxChannel) + ((channels % maxChannel & 0) ? 1 : 0);//单服务器分多次
Action&List&string&, CancellationToken& doSave = (data, ct) =&
if (ct.IsCancellationRequested) return;
var msg = 0;
Connect2WCF.RunSync(sc =& msg = sc.UpdateMailState(data, state));
if (msg == -1)
MessageBox.Show("保存失败了?详情可以查数据库日志表");
else if (msg == 0)
data.ForEach(one =& _emails[one] = true);//标记已经完成的。
new Action(() =& txtEmails.Text = string.Join("\r\n", _emails.Where(x =& !x.Value).Select(x =& x.Key))).InvokeRun(this);
for (int j = 0; j & j++)
if (cts.Token.IsCancellationRequested)
MessageBox.Show("任务取消!");
var currChannel = Math.Min(maxChannel, (channels - j * maxChannel));//两者取其小的
TaskFactory taskFactory = new TaskFactory();
Task[] tasks = new Task[currChannel];
for (int i = 0; i & currC i++)
var subData = tmpEmails.Skip((i + j * maxChannel) * maxLength).Take(maxLength).ToList();
tasks[i] = new Task(() =& doSave(subData, cts.Token), cts.Token);
taskFactory.ContinueWhenAll(tasks,
x =& new Action(() =& lblProgress.Text = "* 已完成 " + ((100 * (k + 1) / times)) + "%").InvokeRun(this), CancellationToken.None);
Array.ForEach(tasks, x =& x.Start());
SetControlEnable(true);
用TaskFactory和CancellationTokenSource结合来实现,在保存修改数据上,实现的效果和上面的方法差不多,但是在中间取消的效果上差很多,取消后,不会有&任务取消&的弹框。后台的执行逻辑猜测是这样:由于Task是单开线程跑,所以在btn的事件中, 所有Tasks和TaskFactory的声明基本上是很快就执行完成了的(电脑执行速度来看可能是一瞬间)。至于保存数据的代码,则在每个Task的后台线程中各自执行,此时操作的时间早已经跳出了btn的事件函数,于是,点击取消之后,由于btn的事件函数早已执行完,因此不会出现"任务取消"的弹框。而每个Task的执行受到线程个数的限制以及每个TaskFactory的ContinueWhenAll函数的监视,它们是有先后顺序但是却又无序地执行。点击取消后,可能有几个线程正在执行保存数据的任务,已经跳过了cancel的判断,所以取消的命令不会立刻反应到后台执行中,会有一部分任务在取消后,仍然在运行。而剩下的其他任务会判断cancel之后取消。由于线程的执行速度不是固定的,因此,小数据包保存执行的顺序虽然大概按照增序执行,但是细节的排序可能有些插队。
所以,总体而言TaskFactory的执行顺序不可控。断点不可控。而parallel.Invoke函数只有在传入的Action[]全部执行完之后,才会返回,所以有效的保证了大层面的执行顺序。至于Action[]这个队列执行的顺序,在Parallel里面也是不可控的。
补充:4092条数据,开启一个通道时,TaskFactory:Parallel = 19:25;
开启5个通道时,多次测试的结果为TaskFactory:Parallel = {18,16,15}:{19,16,15},速度差不多。
一个明显的现象:在数据很多的时候,可以清晰的看到TaskFactory中已完成的百分数出现忽大忽小的情况。例如:1,4,7,12,17,6,12,19,23...
另外,Parallel刚开始执行时,有明显的停顿感,猜测可能是启动并行时产生的效率损耗。
如果希望能够操作过程中能暂停处理,可以使用Parallel,它有一个执行主线程,方便随时停止。如果没有暂停需要,而且电脑的核心数不多(只有一个)时,可以考虑用TaskFactory,效率要明显高于Parallel。
阅读(...) 评论()

我要回帖

更多关于 并行开发模式 的文章

 

随机推荐