异步多线程 Task理解
一、简介
Task是.NET Framework4.0 TPL(任务并行库)提供的新的操作线程池线程的封装类。它提供等待、终止(取消)、返回值、完成通知、失败通知、控制执行的先后次序等优化线程操作功能。Task(任务)并不是线程,任务运行的时候需要使用线程,但并不是说任务取代了线程,任务代码是使用底层的线程(Thread或ThreadPool线程)运行的,任务与线程之间并没有一对一的关系。
二、Task创建与启动
Task类创建的任务使用的是后台线程,所以在前台线程全部终止的时候,如果任务还没有全部执行完,就会被被动终止。创建一个新的任务时,任务调度器(默认是基于线程池的TaskScheduler调度器,ThreadPoolTaskScheduler)会找到一个最合适的工作者线程,然后将任务加入该工作者线程的本地队列(每个工作者线程都有对应本地队列),任务所包含的代码会在该线程中运行。

先定义一个任务要执行的方法:
static void NewTask()
{
Console.WriteLine("开始一个任务");
Console.WriteLine("Task id:{0}",Task.CurrentId);
Console.WriteLine("任务执行完成");
}
再创建和启动一个任务,有以下三种方式:
1、使用TaskFactory创建一个任务
TaskFactory tf = new TaskFactory();
//任务就会立即启
Task t1 = tf.StartNew(NewTask);
2、使用Task类的Factory创建一个任务
Task t2 = Task.Factory.StartNew(NewTask);
3、Task构造函数并Start
Task t3 = new Task(NewTask);
t3.Start();
Task t4 = new Task(NewTask, TaskCreationOptions.PreferFairness);
t4.Start();
//因为任务是后台线程,所以我们这里阻塞主线程一秒钟来等待任务全部执行完成
Thread.Sleep(1000);
注意:使用Task类的构造函数(第3种)和TaskFactory类的StartNew()方法(第2种)时,都可以传递TaskCreationOptions枚举中的值。TaskCreationOptions如下:

注意:如果当前Task上的TaskCreationOptions设置为LongRunning的话,这个task就会委托到Thread中去执行,长时间运行的task占用着ThreadPool的线程,这时候ThreadPool为了保证线程充足,会再次开辟一些Thread,如果耗时任务此时释放了,会导致ThreadPool线程过多,上下文切换频繁,所以此时让Task在Thread中执行还是非常不错的选择,当然不指定这个LongRunning的话,就是在ThreadPool上执行。
三、Task状态与生命周期
任务Task有以下代表任务完成时状态的属性:
1、IsCanceled,因为被取消而完成
2、IsCompleted,成功完成
3、IsFaulted,因为发生异常而完成
任务并没有提供回调事件来通知完成(像BackgroundWorker一样),通过启用一个新任务的方式来完成类似的功能。 ContinueWith方法可以在一个任务完成的时候发起一个新任务,天然支持了任务的完成通知,可以在新任务中获取原任务的结果值。



四、Parallel
使用Parallel.For、Parallel.ForEach的循环迭代的并行执行,TPL会在后台创建System.Threading.Tasks.Task的实例。使用Parallel.Invoke时,TPL也会创建与调用的委托数目一致的System.Threading.Tasks.Task的实例。
五、Task异常处理
当很多任务并行运行的时候,可能会并行发生很多异常。Task实例能够处理一组一组的异常,这些异常有System.AggregateException类处理。
class Program
{
private static ConcurrentQueue<Product> queue = null;
static void Main(string[] args)
{
queue = new ConcurrentQueue<Product>();
System.Threading.CancellationTokenSource token = new CancellationTokenSource();
Task tk1 = Task.Factory.StartNew(() => SetProduct(token.Token));
Thread.Sleep();
//检查状态,是否因为异常而导致失败
if (tk1.IsFaulted)
{
//循环输出异常
foreach (Exception ex in tk1.Exception.InnerExceptions)
{
Console.WriteLine("tk1 Exception:{0}", ex.Message);
}
}
Console.ReadLine();
} static void SetProduct(System.Threading.CancellationToken ct)
{
for (int i = ; i < ; i++)
{
throw new Exception(string.Format("Exception Index {0}", i));
}
Console.WriteLine("SetProduct 执行完成");
}
}
六、Task取消
通过取消标记来中断Task实例的执行。 CancellationTokenSource,CancellationToken下的IsCanceled属性标志当前是否已经被取消,取消任务,任务也不一定会马上取消。
class Program
{
private static ConcurrentQueue<Product> queue = null;
/* coder:释迦苦僧 */
static void Main(string[] args)
{
queue = new ConcurrentQueue<Product>();
System.Threading.CancellationTokenSource token = new CancellationTokenSource();
Task tk1 = Task.Factory.StartNew(() => SetProduct(token.Token));
Task tk2 = Task.Factory.StartNew(() => SetProduct(token.Token));
Thread.Sleep();
//取消任务操作
token.Cancel();
try
{
//等待完成
Task.WaitAll(new Task[] { tk1, tk2 });
}
catch (AggregateException ex)
{
//如果当前的任务正在被取消,那么还会抛出一个TaskCanceledException异常,这个异常包含在AggregateException异常中
Console.WriteLine("tk1 Canceled:{0}", tk1.IsCanceled);
Console.WriteLine("tk1 Canceled:{0}", tk2.IsCanceled);
} Thread.Sleep();
Console.WriteLine("tk1 Canceled:{0}", tk1.IsCanceled);
Console.WriteLine("tk1 Canceled:{0}", tk2.IsCanceled);
Console.ReadLine();
}
static void SetProduct(System.Threading.CancellationToken ct)
{
//每一次循环迭代,都会有新的代码调用 ThrowIfCancellationRequested
//这行代码能够对 OpreationCanceledException 异常进行观察
//并且这个异常的标记与Task实例关联的那个标记进行比较,如果两者相同 ,而且IsCancelled属性为True,那么Task实例就知道存在一个要求取消的请求,并且会将状态转变为Canceled状态,中断任务执行。
//如果当前的任务正在被取消,那么还会抛出一个TaskCanceledException异常,这个异常包含在AggregateException异常中
//检查取消标记
ct.ThrowIfCancellationRequested();
for (int i = ; i < ; i++)
{
Product model = new Product();
model.Name = "Name" + i;
model.SellPrice = i;
model.Category = "Category" + i;
queue.Enqueue(model); ct.ThrowIfCancellationRequested();
}
Console.WriteLine("SetProduct 执行完成");
}
}
class Product
{
public string Name { get; set; }
public string Category { get; set; }
public int SellPrice { get; set; }
}
七、Task.WaitAll 并限定时长
10毫秒没有完成任务,则输出了****
queue = new ConcurrentQueue<Product>();
Task tk1 = new Task(() => { SetProduct(); SetProduct();});
Task tk2 = new Task(() => SetProduct());
tk1.Start();
tk2.Start(); //如果tk1 tk2 没能在10毫秒内完成 则输出 *****
if (!Task.WaitAll(new Task[] { tk1, tk2 }, ))
{
Console.WriteLine("******");
} Console.ReadLine();
八、Task返回值 Task<TResult>
class Program
{ static void Main(string[] args)
{
Task<List<Product>> tk1 = Task<List<Product>>.Factory.StartNew(() => SetProduct());
Task.WaitAll(tk1);
Console.WriteLine(tk1.Result.Count);
Console.WriteLine(tk1.Result[].Name);
Console.ReadLine();
}
static List<Product> SetProduct()
{
List<Product> result = new List<Product>();
for (int i = ; i < ; i++)
{
Product model = new Product();
model.Name = "Name" + i;
model.SellPrice = i;
model.Category = "Category" + i;
result.Add(model);
}
Console.WriteLine("SetProduct 执行完成");
return result;
}
}
九、连续执行多个任务 ContinueWith
class Program
{ static void Main(string[] args)
{
//创建任务t1
Task t1 = Task.Factory.StartNew(() =>
{
Console.WriteLine("执行 t1 任务");
SpinWait.SpinUntil(() =>
{
return false;
}, ); });
//创建任务t2 t2任务的执行 依赖与t1任务的执行完成
Task t2 = t1.ContinueWith((t) =>
{
Console.WriteLine("执行 t2 任务");
SpinWait.SpinUntil(() =>
{
return false;
}, ); });
//创建任务t3 t3任务的执行 依赖与t2任务的执行完成
Task t3 = t2.ContinueWith((t) =>
{
Console.WriteLine("执行 t3 任务");
});
Console.ReadLine();
}
}
异步多线程 Task理解的更多相关文章
- .Net进阶系列(13)-异步多线程(Task和Parallel)(被替换)
一. Task开启多线程的三种形式 1. 利用TaskFactory下的StartNew方法,向StartNew传递无参数的委托,或者是Action<object>委托. 2. 利用Tas ...
- .NET 异步多线程,Thread,ThreadPool,Task,Parallel,异常处理,线程取消
今天记录一下异步多线程的进阶历史,以及简单的使用方法 主要还是以Task,Parallel为主,毕竟用的比较多的现在就是这些了,再往前去的,除非是老项目,不然真的应该是挺少了,大概有个概念,就当了解一 ...
- c#中@标志的作用 C#通过序列化实现深表复制 细说并发编程-TPL 大数据量下DataTable To List效率对比 【转载】C#工具类:实现文件操作File的工具类 异步多线程 Async .net 多线程 Thread ThreadPool Task .Net 反射学习
c#中@标志的作用 参考微软官方文档-特殊字符@,地址 https://docs.microsoft.com/zh-cn/dotnet/csharp/language-reference/toke ...
- NET 异步多线程,THREAD,THREADPOOL,TASK,PARALLEL
.NET 异步多线程,THREAD,THREADPOOL,TASK,PARALLEL,异常处理,线程取消 今天记录一下异步多线程的进阶历史,以及简单的使用方法 主要还是以Task,Parallel为主 ...
- .NET异步多线程,Thread,ThreadPool,Task,Parallel,异常处理,线程取消
今天记录一下异步多线程的进阶历史,以及简单的使用方法 主要还是以Task,Parallel为主,毕竟用的比较多的现在就是这些了,再往前去的,除非是老项目,不然真的应该是挺少了,大概有个概念,就当了解一 ...
- 异步多线程 Thread ThreadPool Task
一.线程 Thread ThreadPool 线程是Windows任务调度的最小单位,线程是程序中的一个执行流,每个线程都有自己的专有寄存器(栈指针.程序计数器等),但代码区是共享的,即不同的线程可以 ...
- Task/Parallel实现异步多线程
代码: #region Task 异步多线程,Task是基于ThreadPool实现的 { //TestClass testClass = new TestClass(); //Action<o ...
- 初探.net framework 下的异步多线程
初探.net framework 下的异步多线程 目录 1.多线程的出现条件 2.Thread和ThreadPool的相关Api及用法 3.Task和Parallel的相关Api及用法 4.Async ...
- 基于任务的异步编程(Task,async,await)
这节讲一下比较高级的异步编程用法Task,以及两个异步关键字async和await. Task是在C#5.0推出的语法,它是基于任务的异步编程语法,是对Thread的升级,也提供了很多API,先看一下 ...
随机推荐
- 如何为linux系统设置全局的默认网络代理
方法1:更改全局配置文件/etc/profile all_proxy="all_proxy=socks://proxy.xxx.com.cn:80/" ftp_proxy=&quo ...
- Maximum Swap LT670
Given a non-negative integer, you could swap two digits at most once to get the maximum valued numbe ...
- Python导入自定义类时显示错误:attempted relative import beyond top-level package
显示这个错误可能有两个原因: 1.文件夹中没有包含__init__.py文件,该文件可以为空,但必须存在该文件. 2.把该文件当成主函数入口,该文件所在文件夹不能被解释器视作package,所以可能导 ...
- 【Redis】Redis cluster集群搭建
Redis集群基本介绍 Redis 集群是一个可以在多个 Redis 节点之间进行数据共享的设施installation. Redis 集群不支持那些需要同时处理多个键的 Redis 命令, 因为执行 ...
- navigator - 定时器 - event
1. navigator userAgent: 包含浏览器名称,内核,版本号的字符串 鄙视: 如何判断浏览器名称和版本号 2. 定时器: 2种: 1. 周期性定时器: 什么是: 让程序每隔一段时间间隔 ...
- PHP字符串函数运用小案例
$str = 'ZenD_CONTRollER_FronT'; //转换为Zend_Controller_Front //1.全部转换为小写 $str1 = strtolower($str); //2 ...
- day08作业---函数
'''2.写函数,检查获取传入列表或元组对象的所有奇数位索引对应的元素,并将其作为新列表返回给调用者.'''#学会了 原来 range(len(iter)) 是 从零到len-1 的数的组合 建新放在 ...
- 前端html的简单认识
一.html 超文本标记语言 hypertext markup language 二.html的结构 三.html标签格式 1.标签由<>把关键字括起来 2.标签通常是成对出现的 , eg ...
- ant Design和ant Design mobile的使用,并实现按需加载
1.全局安装yarn npm install -g create-react-app yarn 2.创建react项目,并用yarn start 运行 3.引入antd/引入antd-mobile y ...
- shell知识积累
Ubuntu下常用的快捷键:https://blog.csdn.net/u010771356/article/details/53543041 变量名和等号之间不能有空格,变量名中间不能有空格,可以使 ...