(说明:随笔内容为学习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. python之面试复习

    待整理:osi七层协议,tcp三次握手四次挥手 1.Http协议(超文本传输协议) 是一种传输数据的格式. 建立在TCP之上 一次请求一次响应,然后断开连接(短连接,无状态) 请求:请求头 \r\n\ ...

  2. 【linux终端操作】

    1. ctr + alt + t 打开新的终端窗口2. ctr + shift + + 终端窗口字体放大3. ctr + - 终端窗口字体缩小4. ls : 查看目录下的文件信息5. pwd: 查看目 ...

  3. 使用extjs的页面弹出窗口宽度不能自适应如何解决?

    1.资源趋势详情下钻页面宽度不能自适应,无法点击关闭按钮 var detailWindow = Ext.create("App.view.com.huawei.drp.qoe.vivid.C ...

  4. 02使用java脚本向Pxc集群写入数据

    使用java脚本向Pxc集群写入数据 批量写入pxc集群程序 导入mysql驱动包 # 批量插入数据的java脚本 package pxc_demo; import java.sql.Connecti ...

  5. linux常用基础命令(一)

    Rz命令 rz命令本地上传文件到服务器: rz在弹出的框中选择文件,上传文件 sz命令 sz命令发送文件到本地: Sz文件名 例:将文件file1 Sz file1 Tomcat启动/关闭命令 比如t ...

  6. Laravel-google-authenticator--Google验证码

    开发前的准备 安装Laravel 安装二维码生成器QrCode,没有安装也可以,接下来会安装 安装拓展 1.运行如下代码安装拓展包: composer require "earnp/lara ...

  7. 插入MongoDB文档:mongo控制台查看插入到MongoDB文档中的内容

    const MongoClient = require('mongodb').MongoClient; const assert = require('assert'); const url = 'm ...

  8. 【JS】Javascript数组操作

    1.提取数组中每个对象的某个属性组成新的数组 如数组为: let arry = [ { name: 'zhao', 'age': 18 }, { name: 'qian', 'age': 19 }, ...

  9. systemctl: command not found

    可以使用service代替 service语法有一点区别 这里演示了错误语法和正确语法

  10. chrome启动参数之

    --remote-debugging-port Chrome 启动的时候,默认是关闭了调试端口的,如果要对一个目标 Chrome PC 浏览器进行调试,那么启动的时候,可以通过传递参数来开启 Chro ...