本文作为 .Net 并行计算 的第二篇
任务并行是指一个或者多个独立的任务同时运行。任务并行类似于多线程或者ThreadPool 工作项。但是抽象的级别更高
1.隐式创建和运行任务。Parallel.Invoke方法提供了一种简便方式,可同时运行任意数量的任意语句。class="sentence" data-source="Just pass in an <span><span class="mtpsTagOuterHtml" ><span>Action</span></span></span> delegate for each item of work." data-guid="8f6bd07894e8b7e41bf35baf174af946">只需为每个工作项传入 Action 委托即可 大家可以想一下了例子的运行结果是什么? 会是输出 时间2000 吗 。如果你这么想就错了。笔者的电脑上输出的结果为 时间1005 (可能在你的电脑上的结果和我一定相同)因为他并不是一个顺序执行的过程。
Stopwatch watch1 = Stopwatch.StartNew();
Parallel.Invoke(() => { Thread.Sleep(1000); }, () => { Thread.Sleep(1000); }); Console.WriteLine("时间"+watch1.ElapsedMilliseconds);
2.显示创建和运行任务
不返回值的任务由 System.Threading.Tasks.Task 类表示。返回值的任务由 System.Threading.Tasks.Task<TResult>类表示,该类从Task继承thread throughout the lifetime of the task." data-guid="18a45a50d09d2acd703988bfae80c515">任务对象处理基础结构详细信息,并提供可在任务的整个生存期内从调用线程访问的方法和属性。下面的例子演示创建一个不返回的任务,和一个返回值的任务
var task = new Task(() => { Thread.Sleep(1000); }); task.Start(); var task1 = new Task<int>(() => { Thread.Sleep(1000); return 100; }); task1.Start(); Console.WriteLine(task1.Result); Console.ReadLine();
我们经常会面对这样一个场景。在一个操作完成后调用另一个的操作并将结果传递其中,我们日常编程中我们一般使用回调来弄。那么我们要怎么样在并行中来处理这个场景了。并行库中的“延续任务”提供了同样功能。 延续任务是一个异步任务,由另一个任务(称为前面的任务)在完成时调用。 下面的例子将使用TaskA.ContinueWith演示创建延续
Task<int> TaskA = new Task<int>(() => { return DateTime.Now.Year; }); Task<String> TaskB = TaskA.ContinueWith((a) => { return String.Format("今年是{0}年", a.Result); }); TaskA.Start(); Console.WriteLine(TaskB.Result);
在上面这个例子中TaskB 必须要TaskA 完成之后。才能得到正确结果。这只是延续的一个非常简单的例子,当然你也可以创建一个多任务延续你可以在多个,或者全部的任务完成之后来运行 这里我还是以上面的取消年月为例子给大家演示一下
Task<int>[] tasks = new Task<int>[2]; tasks[0]= new Task<int>(() => { return DateTime.Now.Year; }); tasks[1] = new Task<int>(() => { return DateTime.Now.Month; }); var continuation = Task.Factory.ContinueWhenAll( tasks, (antecedents) => { string r = string.Format("现在是{0}年{1}月", antecedents[0].Result, antecedents[1].Result); Console.WriteLine(r); }); tasks[0].Start(); tasks[1].Start(); continuation.Wait();
子任务(或嵌套任务)是在另一个任务(称为父任务)的用户委托中创建的Task实例。 can be either detached or attached." data-guid="89104384ea0ee16afe06c677457eb3a5">子任务可分离或附加。 分离的子任务是独立于其父任务执行的任务。 created with the <span><span class="mtpsTagOuterHtml" ><span>TaskCreationOptions<span class="mtpsTagOuterHtml" xmlns=""><span>.</span></span>AttachedToParent</span></span></span> option." data-guid="85c3ce7207b7cb832ff59c3be81907d2">附属子任务是使用 TaskCreationOptions.AttachedToParent 选项创建的嵌套任务。 system resources." data-guid="6ca421e8fc40dce9102a5f7b71c3aeb2">一个任务可以创建任意数量的嵌套和分离子任务,该数量仅受系统资源限制
var parent = Task.Factory.StartNew(() => { Console.WriteLine("父任务"); var child = Task.Factory.StartNew(() => { Thread.Sleep(5000); Console.WriteLine("子任务"); }); }); parent.Wait(); Console.WriteLine("大家都完了.");
大家想不想知道,上面的执行结果是怎么样了 "父任务 子任务 大家都完了" 还是“父任务 大家都完了 子任务" 你可以把这段代码考到vs中,你就会发现是后者从上面的例子中我们可以得出以下结论 父任务是不等待子任务完成的 他还有以下特性 父任务不传播子任务引发的异常。父级状态不依赖于子状态。
1.简单从委托中返回。其实在多数情况下这样已经够了。但是这样取消的任务实例不会转为Canceled状态而是转为RanToCompletion状态
2.引发 OperationCanceledException
var tokenSource = new CancellationTokenSource(); CancellationToken ct = tokenSource.Token; var task = Task.Factory.StartNew(() => { ct.ThrowIfCancellationRequested(); bool moreToDo=true; while (moreToDo) { if (ct.IsCancellationRequested) { ct.ThrowIfCancellationRequested(); } } }, tokenSource.Token); tokenSource.Cancel(); try { task.Wait(); } catch (AggregateException e) { foreach (var v in e.InnerExceptions) Console.WriteLine(e.Message + " " + v.Message); }
下一篇将讲一下并行Linq 欢迎交流拍砖