TPL Part 1 Task 的使用

创建Task

//1. 使用Action
Task task1 = new Task(newAction(printMessage));
// 使用匿名代理
Task task2 = new Task(delegate {
printMessage();
});
// 使用Lambda
Task task3 = new Task(() =>printMessage());
// 使用匿名代理
Task task4 = new Task(() => {
printMessage();
});

还可以使用Factory.StartNew()创建Task,区别在于, Factory.StartNew()主要用于创建简单的,生命周期短的Task。无法Start()已经运行的Task,必须创建1个新Task实例然后Start。

同时运行多个Task,Task Scheduler来决定线程的分配以及Task运行时的顺序。

设置Task参数

    Task task1 = newTask((msg)=>{
    Console.WriteLine(msg);
    },
"Firsttask");
task1.Start();
Console.ReadLine();

传参,打印。

获取Task返回值

Task<int> task1 = new Task<int>(()=> {
Thread.Sleep(3000);
return 1;
});
task1.Start();
Console.WriteLine("Result 1: {0}", task1.Result); //Threadblocked

Task<int> task2 = new Task<int>(obj=> {
Thread.Sleep(3000);
return 2;
}, 100);
task2.Start();
Console.WriteLine("Result 2: {0}", task2.Result);
Console.ReadLine();

在获取Task的Result时,会造成线程阻塞,对于上例来说,task1执行完毕时,Task2才开始执行。

取消Task

CancellationTokenSource tokenSource = new CancellationTokenSource();
CancellationToken token = tokenSource.Token;
Task task = new Task(() => {
for(int i = 0;i < 5; i++) {
Thread.Sleep(1000);
if(token.IsCancellationRequested) {
Console.WriteLine("Task cancel detected");
throw new OperationCanceledException(token);
} else{
Console.WriteLine("Int value {0}", i);
}
}
},token);
task.Start();
Console.WriteLine("Task started , Press enter to cancel task");
Console.ReadLine();
Console.WriteLine("Cancelling task");
tokenSource.Cancel();

先创建了1个TokenSource,将其中的Token传入Action中,在Action中每秒打印1个数字,直到取消Task时,打印并抛出OperationCaceledException。

对于多个Task的情形,还可以将同一个Token传入它们,就可以一次取消多个Task。

监控Task

CancellationTokenSource tokenSource = new CancellationTokenSource();
CancellationToken token = tokenSource.Token;

Task task1 = new Task(() => {
for(int i = 0;i < 5; i++) {
if(token.IsCancellationRequested) {
Console.WriteLine("Task cancel detected");
throw new OperationCanceledException(token);
} else{
Console.WriteLine("Int value {0}", i);
}

Thread.Sleep(1000);
}
},token);
task1.Start();
Console.WriteLine("Task1 started , Press enter to cancel task");

Task task2 = new Task(()=>{
Console.WriteLine("task2 : waiting for task 1 , maximum wait 3 seconds");
token.WaitHandle.WaitOne(3000);
Console.WriteLine("execute task2");
});
task2.Start();

Console.ReadLine();
Console.WriteLine("Cancelling task");
tokenSource.Cancel();

在上例中,开启了两个Task,task2需要等待task1执行完毕或被取消才执行,最大等待时间为3秒。

组合取消Task

对于一些情形,会希望传递多个Token的组合,当任意1个Token 取消了Task,就执行取消:

CancellationTokenSource tokenSource1 = new CancellationTokenSource();
CancellationTokenSource tokenSource2 = new CancellationTokenSource();
CancellationTokenSource tokenSource3 = new CancellationTokenSource();

CancellationTokenSource compositeSource =
CancellationTokenSource.CreateLinkedTokenSource(
tokenSource1.Token,tokenSource2.Token, tokenSource3.Token);

Task task = new Task(() => {
Console.WriteLine("any 1 of 3 token canceled , it will cancel");
compositeSource.Token.WaitHandle.WaitOne();
Console.WriteLine("task canceled");

throw new OperationCanceledException(compositeSource.Token);
},compositeSource.Token);
task.Start();
Console.WriteLine("task started and waiting for cancel");

Thread.Sleep(1000);
Console.WriteLine("token2 canceling");
tokenSource2.Cancel();

Console.ReadLine();

如上例代码所示,3个Token的组合被传入Task中,当Token2取消时,Task被取消,还可以将Token2改为Token1和Token3,不再一一演示。

如果需要在程序外部了解Task是否被Cancel,只需访问IsCanceled属性即可。

等待Task

有时需要在Task外部等待Task执行完毕,可以使用task.Wait()。以下为示例场景:

CancellationTokenSource tokenSource = new CancellationTokenSource();
CancellationTokentoken = tokenSource.Token;

Task task = new Task(()=>{
Console.WriteLine("task : please wait me for 3 secs to complete in main thread");
for(var i = 0 ;i < 3; i++){
Thread.Sleep(1000);
}
Console.WriteLine("task: complete");

},token);
task.Start();

Console.WriteLine("main thread: i do some other jobs...");
Thread.Sleep(1000);
Console.WriteLine("main thread: i am ready to wait...");
task.Wait(1500);

Console.WriteLine("main thread : i continue some other job...");

如上例所示,在Task要求主线程等待3秒,可在主线程中,最多只能等1.5秒,如果task没结束就直接继续主线程中任务的执行。

等待多个任务

在主线程中,需要等待多个线程都执行完毕的情况:

CancellationTokenSource tokenSource = new CancellationTokenSource();
CancellationToken token = tokenSource.Token;

Task task1 = new Task(()=>{
Console.WriteLine("task1 : wait me , main thread");
for(vari = 0 ;i < 3; i++){
Console.WriteLine(string.Format("task1 : {0}/3  done",i+1));
Thread.Sleep(1000);

}
Console.WriteLine("task1: complete");

},token);
task1.Start();

Task task2 = new Task(()=>{
Console.WriteLine("task2 : wait me , main thread");
for(var i = 0 ;i < 2; i++){
Console.WriteLine(string.Format("task2 : {0}/2  done",i+1));
Thread.Sleep(1000);
}
Console.WriteLine("task2: complete");

},token);
task2.Start();

Console.WriteLine("main thread : waiting for task1 and task2");
Task.WaitAll(task1,task2);
Console.WriteLine("main thread : continue my jobs...");

如果需要等待多个任务,任意1个完成就继续,只需将上述代码中的WaitAll改为WaitAny即可,参数一样。

时间: 2024-10-09 07:54:41

TPL Part 1 Task 的使用的相关文章

TPL Part 4 -- Task的协同

简单的Continuation Task.ContinueWith(Task): 当指定的Task执行完毕时. void Main() { Task rootTask = new Task(()=>{ Console.WriteLine("root task completed"); }); root Task.ContinueWith((Task previousTask)=>{ Console.WriteLine("continute task complet

Task的运行原理和工作窃取(work stealing)

在net4.0以前,当调用ThreadPool.QueueUserWorkItem方法往线程池中插入作业时,会把作业内容(其实就是一个委托)放到线程池中的一个全局队列中,然后线程池中的线程按照先进先出的方式取出作业,并处理. 如下图中的方式,主程序创建了Item到Queue中,然后分配到了各个工作线程中.    但 是在.net 4.0以后,线程池做了一些改进,比如增加了TPL(Task Parallel Library),TPL使用到了.net 4.0中新增加的一些特性.这些特性只能通过TPL

.net 异步编程async &amp; await关键字的思考

C# 5.0引入了两个关键字 async和await,这两个关键字在很大程度上帮助我们简化了异步编程的实现代码,而且TPL中的task与async和await有很大的关系 思考了一下异步编程中的async & await关键字,对两个关键字尤其是await关键字一直很迷糊,因此深入思考了一下.首先借助的示例是:[你必须知道的异步编程]C# 5.0 新特性--Async和Await使异步编程更简单这是博客园一个大牛写的,自己也一直关注这个大神,不得不说,博客园大神很多,而且氛围也很好.我引入了其中

.NET 4 并行(多核)编程系列之一入门介绍

本系列文章将会对.NET 4中的并行编程技术(也称之为多核编程技术)以及应用作全面的介绍. 本篇文章的议题如下:  1. 并行编程和多线程编程的区别.  2. 并行编程技术的利弊  3. 何时采用并行编程 系列文章链接: .NET 4 并行(多核)编程系列之一入门介绍 .NET 4 并行(多核)编程系列之二 从Task开始 .NET 4 并行(多核)编程系列之三 从Task的取消 .NET 4 并行(多核)编程系列之四 Task的休眠 .NET 并行(多核)编程系列之五 Task执行和异常处理

C#5.0之后推荐使用TPL(Task Parallel Libray 任务并行库) 和PLINQ(Parallel LINQ, 并行Linq). 其次是TAP(Task-based Asynchronous Pattern, 基于任务的异步模式)

学习书籍: <C#本质论> 1--C#5.0之后推荐使用TPL(Task Parallel Libray 任务并行库) 和PLINQ(Parallel LINQ, 并行Linq). 其次是TAP(Task-based Asynchronous Pattern, 基于任务的异步模式). --用AggregateException处理Task上的未处理异常. --取消任务. CancellationToken --async修饰方法, 返回Task. task.wait(100)可以阻塞现场. a

[多线程]thread,threadpool,task及TPL知识点整理

简单理解 Thread:是一个指令序列,个体对象. Threadpool:在使用Thread的过程中,程序员要为每个希望并发的序列new一个线程,很麻烦,因此希望有一个统一管理线程的方法,程序员就不需要关注线程的申请管理问题,所以就对Thread进行一系列封装,有了ThreadPool.使用Threadpool,把需要并发的序列添加进线程池,线程池根据其线程列表中的线程的空闲情况,动态为并发序列申请线程. Task:再后来,程序员发现在使用Threadpool的过程当中还是存在很多不便,比如:(

TPL(Task Parallel Library)多线程、并发功能

The Task Parallel Library (TPL) is a set of public types and APIs in the System.Threading and System.Threading.Tasks namespaces. The purpose of the TPL is to make developers more productive by simplifying the process of adding parallelism and concurr

使用TPL取回Task中的运行结果的三种方式

概念:TPL( Task Parallel Library) 任务并行库 使用Task类执行多线程操作要比直接使用自己手工创建Thread效率高很多. 默认情况下,TPL使用线程池中的线程执行Task,但是工作结束之后,调用者线程怎么取回执行线程的工作结果呢? 这里有三种方法: 1.使用经典的线程同步手段: 可以使用线程同步对象.比如ManualResetEvent 在任务方法中设置ManualResetEvent状态为Signaled 调用者示例代码: 示例代码: /// <summary>

统计大文件里,频数最高的10个单词,(C# TPL DataFlow版)

最近公司搞了一个写程序的比赛,要求从2G的文件里统计出出现频率最高的10个单词. 最开始的想法是使用字典树,后来发现字典树更适合用在找前缀上,在查找没有hash表效率高. 之后使用Hash表+DataFlow完成了功能,2G的文件处理在20秒以内(其实我有信心优化到10秒以内,但是太折腾了). 这是我的设计图: 为什么要形成那么多结果?因为我不想写锁,写锁会降低很多效率,而且也失去了线程的意义,每个线程做自己的工作, 最后在把每个线程处理的结果汇总起来,这样也符合fork join 的设计. 而