菜鸟学习C#并行编程.docx
《菜鸟学习C#并行编程.docx》由会员分享,可在线阅读,更多相关《菜鸟学习C#并行编程.docx(18页珍藏版)》请在冰豆网上搜索。
菜鸟学习C#并行编程
C#并行编程-Task-菜鸟学习并行编程
菜鸟学习并行编程,参考《C#并行编程高级教程.PDF》,如有错误,欢迎指正。
任务简介
TPL引入新的基于任务的编程模型,通过这种编程模型可以发挥多核的功效,提升应用程序的性能,不需要编写底层复杂且重量级的线程代码。
但需要注意:
任务并不是线程(任务运行的时候需要使用线程,但并不是说任务取代了线程,任务代码是使用底层的线程(软件线程,调度在特定的硬件线程或逻辑内核上)运行的,任务与线程之间并没有一对一的关系。
)
创建一个新的任务时,调度器(调度器依赖于底层的线程池引擎)会使用工作窃取队列找到一个最合适的线程,然后将任务加入队列,任务所包含的代码会在一个线程中运行。
如图:
System.Threading.Tasks.Task
一个Task表示一个异步操作,Task提供了很多方法和属性,通过这些方法和属性能够对Task的执行进行控制,并且能够获得其状态信息。
Task的创建和执行都是独立的,因此可以对关联操作的执行拥有完全的控制权。
使用Parallel.For、Parallel.ForEach的循环迭代的并行执行,TPL会在后台创建System.Threading.Tasks.Task的实例。
使用Parallel.Invoke时,TPL也会创建与调用的委托数目一致的System.Threading.Tasks.Task的实例。
注意项
程序中添加很多异步的操作作为Task实例加载的时候,为了充分利用运行时所有可用的逻辑内核,任务调度器会尝试的并行的运行这些任务,也会尝试在所有的可用内核上对工作进行负载均衡。
但在实际的编码过程当中,并不是所有的代码片段都能够方便的用任务来运行,因为任务会带来额外的开销,尽管这种开销比添加线程所带来的开销要小,但是仍然需要将这个开销考虑在内。
Task状态与生命周期
一个Task实例只会完成其生命周期一次,当Task到达它的3种肯呢过的最终状态之一是,就无法回到之前的任何状态
下面贴代码,详解见注释,方便大家理解Task的状态:
classProgram
{
/*coder:
释迦苦僧*/
staticvoidMain(string[]args)
/*创建一个任务不调用不执行状态为Created*/
Tasktk=newTask(()=>
});
Console.WriteLine(tk.Status.ToString());
/*创建一个任务执行状态为WaitingToRun*/
Tasktk1=newTask(()=>
tk1.Start();/*对于安排好的任务,就算调用Start方法也不会立马启动此时任务的状态为WaitingToRun*/
Console.WriteLine(tk1.Status.ToString());
/*创建一个主任务*/
TaskmainTask=newTask(()=>
SpinWait.SpinUntil(()=>
returnfalse;
},30000);
/*将子任务加入到主任务完成之后执行*/
TasksubTask=mainTask.ContinueWith((t1)=>
/*启动主任务*/
mainTask.Start();
/*此时子任务状态为WaitingForActivation*/
Console.WriteLine(subTask.Status.ToString());
/*创建一个任务执行后等待一段时间并行未结束的情况下状态为Running*/
Tasktk2=newTask(()=>
SpinWait.SpinUntil(()=>false,30000);
tk2.Start();/*对于安排好的任务,就算调用Start方法也不会立马启动*/
SpinWait.SpinUntil(()=>false,300);
Console.WriteLine(tk2.Status.ToString());
/*创建一个任务然后取消该任务状态为Canceled*/
CancellationTokenSourcects=newCancellationTokenSource();
Tasktk3=newTask(()=>
for(inti=0;i{if(!cts.Token.IsCancellationRequested){cts.Token.ThrowIfCancellationRequested();}}},cts.Token);tk3.Start();/*启动任务*/SpinWait.SpinUntil(()=>false,100);cts.Cancel();/*取消该任务执行但并非立马取消所以对于Canceled状态也不会立马生效*/SpinWait.SpinUntil(()=>false,1000);Console.WriteLine(tk3.Status.ToString()+""+tk3.IsCanceled);SpinWait.SpinUntil(()=>false,1000);Console.WriteLine(tk3.Status.ToString()+""+tk3.IsCanceled);SpinWait.SpinUntil(()=>false,1000);Console.WriteLine(tk3.Status.ToString()+""+tk3.IsCanceled);/*创建一个任务让它成功的运行完成会得到RanToCompletion状态*/Tasktk4=newTask(()=>{SpinWait.SpinUntil(()=>false,10);});tk4.Start();SpinWait.SpinUntil(()=>false,300);Console.WriteLine(tk4.Status.ToString());/*创建一个任务让它运行失败会得到Faulted状态*/Tasktk5=newTask(()=>{thrownewException();});tk5.Start();SpinWait.SpinUntil(()=>false,300);Console.WriteLine(tk5.Status.ToString());Console.ReadLine();}}classProduct{publicstringName{get;set;}publicstringCategory{get;set;}publicintSellPrice{get;set;}}使用任务来对代码进行并行化使用Parallel.Invoke可以并行加载多个方法,使用Task实例也能完成同样的工作,下面贴代码:classProgram{privatestaticConcurrentQueuequeue=null;/*coder:释迦苦僧*/staticvoidMain(string[]args){queue=newConcurrentQueue();Tasktk1=newTask(()=>{SetProduct(1);SetProduct(3);});Tasktk2=newTask(()=>SetProduct(2));tk1.Start();tk2.Start();Console.ReadLine();}staticvoidSetProduct(intindex){Parallel.For(0,10000,(i)=>{Productmodel=newProduct();model.Name="Name"+i;model.SellPrice=i;model.Category="Category"+i;queue.Enqueue(model);});Console.WriteLine("SetProduct{0}执行完成",index);}}classProduct{publicstringName{get;set;}publicstringCategory{get;set;}publicintSellPrice{get;set;}}等待任务完成Task.WaitAllTask.WaitAll方法,这个方法是同步执行的,在Task作为参数被接受,所有Task结束其执行前,主线程不会继续执行下一条指令,下面贴代码classProgram{privatestaticConcurrentQueuequeue=null;/*coder:释迦苦僧*/staticvoidMain(string[]args){queue=newConcurrentQueue();Tasktk1=newTask(()=>{SetProduct(1);SetProduct(3);});Tasktk2=newTask(()=>SetProduct(2));tk1.Start();tk2.Start();/*等待任务执行完成后再输出======*/Task.WaitAll(tk1,tk2);Console.WriteLine("等待任务执行完成后再输出======");Tasktk3=newTask(()=>{SetProduct(1);SetProduct(3);});Tasktk4=newTask(()=>SetProduct(2));tk3.Start();tk4.Start();/*等待任务执行前输出======*/Console.WriteLine("等待任务执行前输出======");Task.WaitAll(tk3,tk4); Console.ReadLine();}staticvoidSetProduct(intindex){Parallel.For(0,10000,(i)=>{Productmodel=newProduct();model.Name="Name"+i;model.SellPrice=i;model.Category="Category"+i;queue.Enqueue(model);});Console.WriteLine("SetProduct{0}执行完成",index);}}Task.WaitAll限定等待时长queue=newConcurrentQueue();Tasktk1=newTask(()=>{SetProduct(1);SetProduct(3);});Tasktk2=newTask(()=>SetProduct(2));tk1.Start();tk2.Start();/*如果tk1tk2没能在10毫秒内完成则输出******/if(!Task.WaitAll(newTask[]{tk1,tk2},10)){Console.WriteLine("******");}Console.ReadLine();如图10毫秒没有完成任务,则输出了****通过取消标记取消任务通过取消标记来中断Task实例的执行。CancellationTokenSource,CancellationToken下的IsCanceled属性标志当前是否已经被取消,取消任务,任务也不一定会马上取消,下面贴代码:classProgram{privatestaticConcurrentQueuequeue=null;/*coder:释迦苦僧*/staticvoidMain(string[]args){queue=newConcurrentQueue();System.Threading.CancellationTokenSourcetoken=newCancellationTokenSource();Tasktk1=Task.Factory.StartNew(()=>SetProduct(token.Token));Tasktk2=Task.Factory.StartNew(()=>SetProduct(token.Token));Thread.Sleep(10);/*取消任务操作*/token.Cancel();try{/*等待完成*/Task.WaitAll(newTask[]{tk1,tk2});}catch(AggregateExceptionex){/*如果当前的任务正在被取消,那么还会抛出一个TaskCanceledException异常,这个异常包含在AggregateException异常中*/Console.WriteLine("tk1Canceled:{0}",tk1.IsCanceled);Console.WriteLine("tk1Canceled:{0}",tk2.IsCanceled);}Thread.Sleep(2000);Console.WriteLine("tk1Canceled:{0}",tk1.IsCanceled);Console.WriteLine("tk1Canceled:{0}",tk2.IsCanceled);Console.ReadLine();}staticvoidSetProduct(System.Threading.CancellationTokenct){/*每一次循环迭代,都会有新的代码调用ThrowIfCancellationRequested*这行代码能够对OpreationCanceledException异常进行观察*并且这个异常的标记与Task实例关联的那个标记进行比较,如果两者相同,而且IsCancelled属性为True,那么Task实例就知道存在一个要求取消的请求,并且会将状态转变为Canceled状态,中断任务执行。*如果当前的任务正在被取消,那么还会抛出一个TaskCanceledException异常,这个异常包含在AggregateException异常中/*检查取消标记*/ct.ThrowIfCancellationRequested();for(inti=0;i<50000;i++){Productmodel=newProduct();model.Name="Name"+i;model.SellPrice=i;model.Category="Category"+i;queue.Enqueue(model);ct.ThrowIfCancellationRequested();}Console.WriteLine("SetProduct执行完成");}}classProduct{publicstringName{get;set;}publicstringCategory{get;set;}publicintSellPrice{get;set;}}Task异常处理当很多任务并行运行的时候,可能会并行发生很多异常。Task实例能够处理一组一组的异常,这些异常有System.AggregateException类处理classProgram{privatestaticConcurrentQueuequeue=null;/*coder:释迦苦僧*/staticvoidMain(string[]args){queue=newConcurrentQueue();System.Threading.CancellationTokenSourcetoken=newCancellationTokenSource();Tasktk1=Task.Factory.StartNew(()=>SetProduct(token.Token));Thread.Sleep(2000);if(tk1.IsFaulted){/*循环输出异常*/foreach(Exceptionexintk1.Exception.InnerExceptions){Console.WriteLine("tk1Exception:{0}",ex.Message);}}Console.ReadLine();}staticvoidSetProduct(System.Threading.CancellationTokenct){for(inti=0;i<5;i++){thrownewException(string.Format("ExceptionIndex{0}",i));}Console.WriteLine("SetProduct执行完成");}}Task返回值TaskclassProgram{/*coder:释迦苦僧*/staticvoidMain(string[]args){Task>tk1=Task>.Factory.StartNew(()=>SetProduct());Task.WaitAll(tk1);Console.WriteLine(tk1.Result.Count);Console.WriteLine(tk1.Result[0].Name);Console.ReadLine();}staticListSetProduct(){Listresult=newList();for(inti=0;i<500;i++){Productmodel=newProduct();model.Name="Name"+i;model.SellPrice=i;model.Category="Category"+i;result.Add(model);}Console.WriteLine("SetProduct执行完成");returnresult;}}通过延续串联多个任务ContinueWith:创建一个目标Task完成时,异步执行的延续程序,await,如代码所示:classProgram{/*coder:释迦苦僧*/staticvoidMain(string[]args){/*创建任务t1*/Taskt1=Task.Factory.StartNew(()=>{Console.WriteLine("执行t1任务");SpinWait.SpinUntil(()=>{returnfalse;},2000);});/*创建任务t2t2任务的执行依赖与t1任务的执行完成*/Taskt2=t1.ContinueWith((t)=>{Console.WriteLine("执行t2任务");SpinWait.SpinUntil(()=>{returnfalse;},2000);});/*创建任务t3t3任务的执行依赖与t2任务的执行完成*/Taskt3=t2.ContinueWith((t)=>{Console.WriteLine("执行t3任务");});Console.ReadLine();}}TaskContinuationOptionsTaskContinuationOptions参数,可以控制延续另一个任的任务调度和执行的可选行为。下面看代码:classProgram{/*coder:释迦苦僧*/staticvoidMain(string[]args){/*创建任务t1*/Taskt1=Task.Factory.StartNew(()=>{Console.WriteLine("执行t1任务");SpinWait.SpinUntil(()=>{returnfalse;},2000);thrownewException("异常");});/*创建任务t2t2任务的执行依赖与t1任务的执行完成*/Taskt2=t1.ContinueWith((t)=>{Console.WriteLine(t.Status);Console.WriteLine("执行t2任务");SpinWait.SpinUntil(()=>{returnfalse;},2000);/*定义TaskContinuationOpt
if(!
cts.Token.IsCancellationRequested)
cts.Token.ThrowIfCancellationRequested();
}
},cts.Token);
tk3.Start();/*启动任务*/
SpinWait.SpinUntil(()=>false,100);
cts.Cancel();/*取消该任务执行但并非立马取消所以对于Canceled状态也不会立马生效*/
SpinWait.SpinUntil(()=>false,1000);
Console.WriteLine(tk3.Status.ToString()+""+tk3.IsCanceled);
/*创建一个任务让它成功的运行完成会得到RanToCompletion状态*/
Tasktk4=newTask(()=>
SpinWait.SpinUntil(()=>false,10);
tk4.Start();
Console.WriteLine(tk4.Status.ToString());
/*创建一个任务让它运行失败会得到Faulted状态*/
Tasktk5=newTask(()=>
thrownewException();
tk5.Start();
Console.WriteLine(tk5.Status.ToString());
Console.ReadLine();
classProduct
publicstringName{get;set;}
publicstringCategory{get;set;}
publicintSellPrice{get;set;}
使用任务来对代码进行并行化
使用Parallel.Invoke可以并行加载多个方法,使用Task实例也能完成同样的工作,下面贴代码:
privatestaticConcurrentQueuequeue=null;
queue=newConcurrentQueue();
Tasktk1=newTask(()=>{SetProduct
(1);SetProduct(3);});
Tasktk2=newTask(()=>SetProduct
(2));
tk1.Start();
tk2.Start();
staticvoidSetProduct(intindex)
Parallel.For(0,10000,(i)=>
Productmodel=newProduct();
model.Name="Name"+i;
model.SellPrice=i;
model.Category="Category"+i;
queue.Enqueue(model);
Console.WriteLine("SetProduct{0}执行完成",index);
等待任务完成Task.WaitAll
Task.WaitAll方法,这个方法是同步执行的,在Task作为参数被接受,所有Task结束其执行前,主线程不会继续执行下一条指令,下面贴代码
/*等待任务执行完成后再输出======*/
Task.WaitAll(tk1,tk2);
Console.WriteLine("等待任务执行完成后再输出======");
Tasktk3=newTask(()=>{SetProduct
Tasktk4=newTask(()=>SetProduct
tk3.Start();
/*等待任务执行前输出======*/
Console.WriteLine("等待任务执行前输出======");
Task.WaitAll(tk3,tk4);
Task.WaitAll限定等待时长
/*如果tk1tk2没能在10毫秒内完成则输出******/
Task.WaitAll(newTask[]{tk1,tk2},10))
Console.WriteLine("******");
如图10毫秒没有完成任务,则输出了****
通过取消标记取消任务
通过取消标记来中断Task实例的执行。
CancellationTokenSource,CancellationToken下的IsCanceled属性标志当前是否已经被取消,取消任务,任务也不一定会马上取消,下面贴代码:
System.Threading.CancellationTokenSourcetoken=newCancellationTokenSource();
Tasktk1=Task.Factory.StartNew(()=>SetProduct(token.Token));
Tasktk2=Task.Factory.StartNew(()=>SetProduct(token.Token));
Thread.Sleep(10);
/*取消任务操作*/
token.Cancel();
try
/*等待完成*/
Task.WaitAll(newTask[]{tk1,tk2});
catch(AggregateExceptionex)
/*如果当前的任务正在被取消,那么还会抛出一个TaskCanceledException异常,这个异常包含在AggregateException异常中*/
Console.WriteLine("tk1Canceled:
{0}",tk1.IsCanceled);
{0}",tk2.IsCanceled);
Thread.Sleep(2000);
staticvoidSetProduct(System.Threading.CancellationTokenct)
/*每一次循环迭代,都会有新的代码调用ThrowIfCancellationRequested
*这行代码能够对OpreationCanceledException异常进行观察
*并且这个异常的标记与Task实例关联的那个标记进行比较,如果两者相同,而且IsCancelled属性为True,那么Task实例就知道存在一个要求取消的请求,并且会将状态转变为Canceled状态,中断任务执行。
*如果当前的任务正在被取消,那么还会抛出一个TaskCanceledException异常,这个异常包含在AggregateException异常中
/*检查取消标记*/
ct.ThrowIfCancellationRequested();
for(inti=0;i<50000;i++)
Console.WriteLine("SetProduct执行完成");
Task异常处理当很多任务并行运行的时候,可能会并行发生很多异常。
Task实例能够处理一组一组的异常,这些异常有System.AggregateException类处理
if(tk1.IsFaulted)
/*循环输出异常*/
foreach(Exceptionexintk1.Exception.InnerExceptions)
Console.WriteLine("tk1Exception:
{0}",ex.Message);
for(inti=0;i<5;i++)
thrownewException(string.Format("ExceptionIndex{0}",i));
Task返回值Task
Task>tk1=Task>.Factory.StartNew(()=>SetProduct());
Task.WaitAll(tk1);
Console.WriteLine(tk1.Result.Count);
Console.WriteLine(tk1.Result[0].Name);
staticListSetProduct()
Listresult=newList();
for(inti=0;i<500;i++)
result.Add(model);
returnresult;
通过延续串联多个任务
ContinueWith:
创建一个目标Task完成时,异步执行的延续程序,await,如代码所示:
/*创建任务t1*/
Taskt1=Task.Factory.StartNew(()=>
Console.WriteLine("执行t1任务");
},2000);
/*创建任务t2t2任务的执行依赖与t1任务的执行完成*/
Taskt2=t1.ContinueWith((t)=>
Console.WriteLine("执行t2任务");
/*创建任务t3t3任务的执行依赖与t2任务的执行完成*/
Taskt3=t2.ContinueWith((t)=>
Console.WriteLine("执行t3任务");
TaskContinuationOptions
TaskContinuationOptions参数,可以控制延续另一个任的任务调度和执行的可选行为。
下面看代码:
thrownewException("异常");
Console.WriteLine(t.Status);
/*定义TaskContinuationOpt
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1