(说明:随笔内容为学习task的笔记,资料来源:https://docs.microsoft.com/en-us/dotnet/api/system.threading.tasks.task?redirectedfrom=MSDN&view=netframework-4.7.2,下面内容的图片大多来自msdn,不是的会说明)

一、什么是task?

Task 是一个独立的操作线程,通常是异步执行的。通过Task启动的异步操作线程是在线程池中执行的,也即Task使用的是线程池的线程。

测试一下Task使用线程池的证明:

代码如下:

        public static void ThreadPoolTest()
{
Action action1 = () => { Console.WriteLine("I am Action, I don't need anything"); };
for (int i = ; i < ; i++)
{
new Task(action1).Start();
}
} public static void ThreadPoolTest2()
{
Action action1 = () => { Console.WriteLine("I am Action, I don't need anything"); };
for (int i = ; i < ; i++)
{
new Thread(new ThreadStart(Print)).Start();
}
} private static void Print()
{
Console.WriteLine("I am Action, I don't need anything");
}

运行结果:

这个是使用Windows Performence Monitor(性能监视器)监视的系统当前的线程数量情况,绿色的线代表的是当前应用程序托管的线程,包括正在运行的和已经停止的。图中绿色的线急剧上升的时候是我开始运行ThreadPoolTest2方法的时候(下降是我关闭程序的时候),而在后面当线程恢复平稳我运行ThreadPoolTest方法的时候线程的数量却没有什么变化。

二、Task的构造函数:

我们主要看以下几个:
1、Task(Action):

Action是没有返回值的委托。

简单来说,直接贴代码,这个是很简单的一个操作,也很常用:

        public static void ActionParameter()
{
Action action1 = () => { Console.WriteLine("I am Action, I don't need anything"); };
Action<object> action2 = (o) => { Console.WriteLine("I am Action, My Name is:"+o); };
Task task1 = new Task(action1);
Task task2 = new Task(action2,"a");
Task task3 = new Task(action2, "b"); task1.Start();
task2.Start();
task3.Start();
}

2、Task(Action, CancellationToken)

先上例子:

 CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
var token = cancellationTokenSource.Token;
List<int> intList = new List<int>();
for (int i = ; i < ; i++)
{
intList.Add(i);
} Task t = Task.Run(() => { Parallel.ForEach(intList,
i => {
Console.WriteLine(i);
Interlocked.Increment(ref num);
Console.WriteLine("num is:" + num);
if (token.IsCancellationRequested)
token.ThrowIfCancellationRequested(); }
);
}, token); Thread.Sleep();
cancellationTokenSource.Cancel();
try
{
await t;
}
catch (AggregateException aggregateExceptions)
{
foreach (var ex in aggregateExceptions.InnerExceptions)
{
Console.WriteLine(ex.GetType().Name + "========================================");
}
}
finally
{
cancellationTokenSource.Dispose();
}

运行结果:


num is:

num is:
num is: num is:
OperationCanceledException========================================
OperationCanceledException========================================
OperationCanceledException========================================
OperationCanceledException========================================

通过上面例子可以看到:
1、进行取消用的是 CancellationTokenSource (官方文档定义:向应该被取消的 System.Threading.CancellationToken 发送信号。)的属性Token(类型是CancellationToken);

2、我们在子线程中是用Parallel(用于并行编程,同时启用多个线程运行),在Parallel中当检查到当前线程已经取消都抛出了异常,但是我们try catch这些异常的地方是在await,这个是很重要的一点,而且,同时启动多个线程,异常是放在AggregateException 中;

3、我们有一行代码Thread.Sleep(5),让主线程睡眠了5毫秒,这个是为了让子线程能够获得时间片。如果不这么做很大的可能会使await等待的是一个已经取消的异步操作,会抛出TaskCanceledException异常,需要进行捕获。有兴趣可以尝试。

4、记得释放CancellationTokenSource

也可以在内部取消线程(仅展示修改的代码块):

            Task t = Task.Run(() => {

                Parallel.ForEach(intList,
i => {
Console.WriteLine(i);
Interlocked.Increment(ref num);
if (token.IsCancellationRequested)
token.ThrowIfCancellationRequested();
if (num > )
{
cancellationTokenSource.Cancel();
}
Console.WriteLine("num is:" + num);
}
);
}, token); try
{
await t;
}

3、Task(Action, TaskCreationOptions)

Action我们已经了解了,主要看的是TaskCreationOptions,我们知道,这种命名的一般是枚举类型,看一下有什么枚举值,官网有解释:

3.1 AttachedToParent

看一下区别:

看示例代码:

 public static async void CreateOptionTest()
{
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
Task task = new Task(() =>
{ Task childTask = new Task(() =>
{
try
{
Thread.Sleep();
Console.WriteLine("child task is completed--------------------------");
throw new ChildIsCompletedException("just need a exception");
}
catch (ChildIsCompletedException ex)
{
throw ex;
}
}
//,TaskCreationOptions.AttachedToParent
);
childTask.Start(); Thread.Sleep();
Console.WriteLine("parent task is completed============================");
}, TaskCreationOptions.None); task.Start();
try
{
await task;
Console.WriteLine("parent task status:"+task.IsCompleted);
}catch(AggregateException aggreEx)
{
IReadOnlyCollection<Exception> exs = aggreEx.InnerExceptions;
foreach (var item in exs)
{
Console.WriteLine(item.GetType().Name);
}
}
}

有添加AttachedToParent的运行结果:

parent task is completed============================
child task is completed--------------------------
ChildIsCompletedException

没有添加的结果:

parent task is completed============================
parent task status:True
child task is completed--------------------------

没有添加到父任务的时候是没有抛出异常的,而且父任务完成了但是它还没有退出,需要等待子任务完成才算完成,然而子任务抛出了异常,所以它就挂了,抛出了子任务的异常,而不附加到父任务的时候,就没有异常抛出,父任务正常完成了。

3.2  DenyChildAttach 使父任务拒绝任何尝试的子任务附加;

在3.1的例子中,我们父任务的TaskCreationOptions的值是None,改成DenyChildAttach,即使子任务使用了AttachedToParent,输出结果也会是像没有添加一样。

4、 属性:

4.1 AsyncState:

AsyncState为传入线程的参数,类型是Object,以委托Action创建线程为例,委托Action可以传入一个参数,AsyncState就是这个参数,请见:

        public static void GetAsyncStateTest()
{
Func<int> func = () => *;
Task task = new Task((x) => { Console.WriteLine("I am Action"); }, "I am AsyncState");
task.Start();
Console.WriteLine(task.AsyncState);
}

运行结果:

I am AsyncState
I am Action

4.2 CompletedTask  (与FromResult<T>(T object)相比)

CompletedTask和FromResult都返回一个状态为RanToCompletion的Task,区别是一个有返回值一个没有。当我们允许一个线程需要返回一个task的时候,如果在早期就已经知道了结果线程不需要继续往下运行可以根据需不需要返回值使用这两个中的一个。

public static void GetCompletedTask()
{
Func<Task<string>> funcTask = () =>
{
for (int i = ; i < ; i++)
{
if (i > )
{
return Task.FromResult<string>("已完成");
}
} return new Task<string>((x) => { return (string)x; }, "全部完成");
}; Func<Task> funcTaskNotResult = () =>
{
for (int i = ; i < ; i++)
{
if (i > )
{
return Task.CompletedTask;
}
} return new Task<string>((x) => { return (string)x; }, "全部完成");
}; Task<string> task=Task.Run<string>(funcTask);
Task taskNotResult = Task.Run(funcTaskNotResult);
Thread.Sleep();
Console.WriteLine("task result:"); Console.WriteLine("status:"+task.Status);
Console.WriteLine("is completed:"+task.IsCompleted);
Console.WriteLine("result"+task.Result);
Console.WriteLine("taskNotResult result:");
Console.WriteLine("status:" + taskNotResult.Status);
Console.WriteLine(taskNotResult.IsCompleted);
}

返回结果:

task result:
status:RanToCompletion
is completed:True
result已完成
taskNotResult result:
status:RanToCompletion
True

持续更新中 ing

C# 异步编程之 Task 的使用的更多相关文章

  1. net异步编程之await

    net异步编程之await 初探asp.net异步编程之await   终于毕业了,也顺利进入一家期望的旅游互联网公司.27号入职.放肆了一个多月没写代码,好方啊. 另外一下观点均主要针对于await ...

  2. python异步编程之asyncio

    python异步编程之asyncio   前言:python由于GIL(全局锁)的存在,不能发挥多核的优势,其性能一直饱受诟病.然而在IO密集型的网络编程里,异步处理比同步处理能提升成百上千倍的效率, ...

  3. 异步编程之Generator(1)——领略魅力

    异步编程系列教程: (翻译)异步编程之Promise(1)--初见魅力 异步编程之Promise(2):探究原理 异步编程之Promise(3):拓展进阶 异步编程之Generator(1)--领略魅 ...

  4. 异步编程之Promise(3):拓展进阶

    异步编程系列教程: (翻译)异步编程之Promise(1)--初见魅力 异步编程之Promise(2):探究原理 异步编程之Promise(3):拓展进阶 异步编程之Generator(1)--领略魅 ...

  5. 异步编程之Promise(2):探究原理

    异步编程系列教程: (翻译)异步编程之Promise(1)--初见魅力 异步编程之Promise(2):探究原理 异步编程之Promise(3):拓展进阶 异步编程之Generator(1)--领略魅 ...

  6. (翻译)异步编程之Promise(1):初见魅力

    原文:https://www.promisejs.org/ by Forbes Lindesay 异步编程系列教程: (翻译)异步编程之Promise(1)--初见魅力 异步编程之Promise(2) ...

  7. Javascript异步编程之setTimeout与setInterval详解分析(一)

    Javascript异步编程之setTimeout与setInterval 在谈到异步编程时,本人最主要会从以下三个方面来总结异步编程(注意:特别解释:是总结,本人也是菜鸟,所以总结不好的,请各位大牛 ...

  8. 异步编程之co——源码分析

    异步编程系列教程: (翻译)异步编程之Promise(1)--初见魅力 异步编程之Promise(2):探究原理 异步编程之Promise(3):拓展进阶 异步编程之Generator(1)--领略魅 ...

  9. 异步编程之Generator(2)——剖析特性

    异步编程系列教程: (翻译)异步编程之Promise(1)--初见魅力 异步编程之Promise(2):探究原理 异步编程之Promise(3):拓展进阶 异步编程之Generator(1)--领略魅 ...

随机推荐

  1. JAVA进阶22

    1.接口默认方法的使用 ①接口的默认方法可以通过接口实现类对象直接调用. ②接口的默认方法也可以被接口实现类进行覆盖重写 package cn.intcast.demo17; public inter ...

  2. 20175204 张湲祯 2018-2019-2《Java程序设计》第八周学习总结

    20175204 张湲祯 2018-2019-2<Java程序设计>第八周学习总结 教材学习内容总结 -第十五章泛型与集合框架要点: 一.泛型 1.泛型(Generics)的主要目的是可以 ...

  3. mac 安装和使用MongoDB

    安装 尝试一:手动命令安装尝试二:采用Homebrew尝试三:下载安装包使用安装尝试一:手动命令安装按照官网https://docs.mongodb.com/manual/tutorial/insta ...

  4. WPF入门之一APP.XAML

    WPF运行之后,App.xaml是应用的声明起始点. 一.指定入口 通过指定Application 的StartupUri属性,指示了启动应用的时候,加载哪个窗口或网页. 最常见的就是将默认的Main ...

  5. js 本地缓存localStorage

    .localStorage - 没有时间限制的数据存储 ,,]; localStorage.setItem("stor",arr); console.log(localStorag ...

  6. 【JS】VUE学习

    VUE的全家桶:vue-cli,vue-router,vue-resource,vuex 环境搭建:https://www.jianshu.com/p/32beaca25c0d 先码在这儿吧. htt ...

  7. margin:auto你真的理解么?

    含义 margin:auto是具有强烈计算意味的关键字,用来计算元素对应方向应该获得的剩余空间大小 填充规则 (1) 如果一侧定值,一侧auto,则auto为剩余空间大小 (2) 如果两侧均是auto ...

  8. docker启动,重启,停止容器

    docker 启动已经停止的容器 docker start 容器ID或容器名 docker 停止容器 docker stop 容器ID或容器名 docker 启动一个容器 -d:后台运行 -p:端口映 ...

  9. 2018-2019-2 网络对抗技术 20165314 Exp3 免杀原理与实践

    免杀原理与实践说明 一.实验说明 任务一:正确使用msf编码器,msfvenom生成如jar之类的其他文件,veil-evasion,自己利用shellcode编程等免杀工具或技巧:(1.5分) 任务 ...

  10. 将Go的main包拆分为多个文件

    将Go的main包拆分为多个文件的写法和普通包是完全一致的,其使用规则也相同.如编写main包结构如下: main |----main.go |----show.go 在main.go中编写了main ...