本文还处于草稿阶段,难免还有错误修改改正,逻辑还不是很清晰,笔者会努力完善,长期更新!

[0000] 前言

标题起得有些"大",意在集大家的力量,总结出来一份关于Task相对"正确"的知识总结,欢迎读者提出宝贵意见!本文内容来自于笔者在编码的时候种种疑问,来自于对异步编程在操作系统中实际运行过程的好奇。平时使用Task战战兢兢,既想提高效率,又怕它不受控制,到处乱来。与其这样,不如此时此刻一起来了解它神秘的面纱吧!Just do IT.

[0001] 为什么要编写异步代码

新型应用广泛使用文件和网络 I/O。 默认情况下 I/O API 一般会阻塞,导致糟糕的用户体验和硬件利用率,除非希望学习和使用富有挑战的模式。 基于任务的异步 API 和语言级异步编程模型改变了这种模型,只需了解几个新概念就可默认进行异步执行。

异步代码具有以下特点:

  • 等待 I/O 请求返回的同时,可通过生成处理更多请求的线程,处理更多的服务器请求。
  • 等待 I/O 请求的同时生成 UI 交互线程,并通过将长时间运行的工作转换到其他 CPU 核心,让 UI 的响应速度更快。
  • 许多较新的 .NET APIs 都是异步的。
  • 在 .NET 中编写异步代码很简单!

来源: https://docs.microsoft.com

[0010] 关于C#中的异步编程模式

.NET 提供了执行异步操作的三种模式:

  • 基于任务的异步模式 (TAP) ,该模式使用单一方法表示异步操作的开始和完成。 TAP 是在 .NET Framework 4 中引入的。 这是在 .NET 中进行异步编程的推荐方法。 C# 中的 async 和 await 关键词以及 Visual Basic 中的 Async 和 Await 运算符为 TAP 添加了语言支持。 有关详细信息,请参阅基于任务的异步模式 (TAP)

  • 基于事件的异步模式 (EAP) ,是提供异步行为的基于事件的旧模型。 这种模式需要后缀为 Async 的方法,以及一个或多个事件、事件处理程序委托类型和 EventArg 派生类型。 EAP 是在 .NET Framework 2.0 中引入的。 建议新开发中不再使用这种模式。 有关详细信息,请参阅基于事件的异步模式 (EAP)

  • 异步编程模型 (APM) 模式(也称为 IAsyncResult 模式),这是使用 IAsyncResult 接口提供异步行为的旧模型。 在这种模式下,同步操作需要 BeginEnd 方法(例如,BeginWriteEndWrite以实现异步写入操作)。 不建议新的开发使用此模式。 有关详细信息,请参阅异步编程模型 (APM)

模式的比较

为了快速比较这三种模式的异步操作方式,请考虑使用从指定偏移量处起将指定量数据读取到提供的缓冲区中的Read方法:

public class MyClass
{
public int Read(byte [] buffer, int offset, int count);
}

此方法对应的 TAP 将公开以下单个 ReadAsync 方法:

public class MyClass
{
public Task<int> ReadAsync(byte [] buffer, int offset, int count);
}

对应的 EAP 将公开以下类型和成员的集:

public class MyClass
{
public void ReadAsync(byte [] buffer, int offset, int count);
public event ReadCompletedEventHandler ReadCompleted;
}

对应的 APM 将公开 BeginReadEndRead 方法:

public class MyClass
{
public IAsyncResult BeginRead(
byte [] buffer, int offset, int count,
AsyncCallback callback, object state);
public int EndRead(IAsyncResult asyncResult);
}

来源: https://docs.microsoft.com

分割线,未完区域--------------------------------

[0011] 实践

使用Async

请一路Async,否则会不可控。

网络请求,文件读写时系统自带的Async方法不会创建多线程,而是使用完成端口,依靠中断来实现!

线程池中的线程分为 WorkerThread 和 CompletionPortThread .

平时我们使用的线程是WorkerThread,IO读写使用的是CompletionPortThread

1. 创建IO密集型任务

以下代码不会创建多个线程(WorkerThread),代码会在当前线程工作,且不会堵塞哦。

执行起来非常类似同步程序, 使用 await RunActionAsync(()=>{});

后,会立即执行程序

public Task RunActionAsync(Action action)
{
TaskCompletionSource<Task> source = new TaskCompletionSource<Task>(TaskCreationOptions.AttachedToParent);
Task<Task> task = source.Task; try
{
action.Invoke();
}
catch (Exception ex)
{
source.SetException(ex);
} source.SetResult(Task.CompletedTask); return task;
}

2. 计算密集型任务

以下代码会创建新线程(WorkerThread),位于线程池,线程池默认最小WorkerThread为CPU核心数,CompletionPortThread为1000(实际最小值依实际运行情况而定,可手工修改)

运行时并不会立即执行Action,按照默认执行计划(TaskScheduler.Default执行,比如用for循环一堆Task.Run(async ()=> {await httpgetAsync(); echo(i); )任务,执行时你会发现i都是最后一个值

await Task.Run(()=>{});

以下代码会创建新线程(WorkerThread),在不在ThreadPool关键在于TaskCreationOptions枚举,如果为LongRunning,则直接会创建一个非线程池的线程执行任务,如果不是,则会在线程池里寻找线程,如果没有,会在线程池里新申请线程(创建一个耗时一秒),执行任务。

会立即执行Action

Task.Factory.StartNew(_ =>
{
action.Invoke();
},
null,
CancellationToken.None,
TaskCreationOptions.LongRunning,
TaskScheduler.Default)

3.一些坑

以下代码.NET Core不支持哦,请使用Task.Factory.StartNew代替

Task.Factory.FromAsync(
new Func<AsyncCallback, object, IAsyncResult>((cb, obj) => action.BeginInvoke(biz, cb, obj)),
new Action<IAsyncResult>(ar => action.EndInvoke(ar)), null)

参考

The danger of TaskCompletionSource class

了解 .NET 的默认 TaskScheduler 和线程池(ThreadPool)设置,避免让 Task.Run 的性能急剧降低

.NET 中小心嵌套等待的 Task,它可能会耗尽你线程池的现有资源,出现类似死锁的情况

.NET 中使用 TaskCompletionSource 作为线程同步互斥或异步操作的事件

定义一组抽象的 Awaiter 的实现接口,你下次写自己的 await 可等待对象时将更加方便

.NET 除了用 Task 之外,如何自己写一个可以 await 的对象?

.NET 中什么样的类是可使用 await 异步等待的?

Asynchronous I/O in C#: I/O Completion Ports

Asynchronous I/O in C#: I/O Completion Ports

Migrating Delegate.BeginInvoke Calls for .NET Core

声明

本文采用知识共享署名-非商业性使用-相同方式共享 2.5 中国大陆许可协议进行许可,发表在CSDN博客园,欢迎读者转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接!请读者/爬虫们尊重版权

# C# 中的Task创建指南的更多相关文章

  1. 【手记】MTK之TASK创建及使用

    首先来看看task的数据类型声明,在config\include\hal\task_config.h中对task和module类型进行了定义. /*************************** ...

  2. Netty中NioEventLoopGroup的创建源码分析

    NioEventLoopGroup的无参构造: public NioEventLoopGroup() { this(0); } 调用了单参的构造: public NioEventLoopGroup(i ...

  3. Pycharm在Ubuntu14.04中的基本使用指南

    前几天给大家分享了:如何在VMware虚拟机中安装Ubuntu14.04系统.今天给大家分享一下在Ubuntu14.04中如何简单的使用Pycharm.1.启动Pycharm,将进入Pycharm的启 ...

  4. 深入理解gradle中的task

    目录 简介 定义task tasks 集合类 Task 之间的依赖 定义task之间的顺序 给task一些描述 task的条件执行 task rule Finalizer tasks 总结 深入理解g ...

  5. 一、neo4j中文文档-入门指南

    目录 neo4j中文文档-入门指南 Neo4j v4.4 neo4j **Cypher ** 开始使用 Neo4j 1. 安装 Neo4j 2. 文档 图数据库概念 1. 示例图 2.节点 3. 节点 ...

  6. JVM中对象的创建过程

    JVM中对象的创建过程如以下流程图中所示: 对其主要步骤进行详细阐述: 为新生对象分配内存: 内存的分配方式: 指针碰撞:假设Java堆中内存是绝对规整的,所有用过的内存放在一边,空闲的内存在另一边, ...

  7. C/C++中动态链接库的创建和调用

    DLL 有助于共享数据和资源.多个应用程序可同时访问内存中单个DLL 副本的内容.DLL 是一个包含可由多个程序同时使用的代码和数据的库.下面为你介绍C/C++中动态链接库的创建和调用. 动态连接库的 ...

  8. ora-01658 :无法为表空间USERS 中的段创建INITIAL区

    "CREATE INDEX "IDX_TS_BONUS_Q_201209_DS" ON "TS_BONUS_Q_201209" ("DS&q ...

  9. (转).NET 4.5中使用Task.Run和Parallel.For()实现的C# Winform多线程任务及跨线程更新UI控件综合实例

    http://2sharings.com/2014/net-4-5-task-run-parallel-for-winform-cross-multiple-threads-update-ui-dem ...

随机推荐

  1. unity,C#,游戏面试笔试真题

    最开始的两家公司笔试面试题目 一家公司是学校聘请研究教育方面VR课件的公司,面试没几天,就收到了面试通过的消息,后面因为通过了另一家游戏公司而拒绝了. 另一家公司是一家游戏外企,在春熙路,当时笔试还可 ...

  2. HDU 6315

    题意略. 思路:本题的思路总的来说就是暴力 + 剪枝. 我们依然用线段树来维护: 定义结点node{ l , r , minn , contirbute} 分别为某个区间的左右端点,和该区间(b序列) ...

  3. .NET平台下,钉钉微应用开发之:工作消息通知

    首先看下官方文档,为我们提供了POST请求地址,和几个必传参数的列表以及参数示例,写的都挺详细的. 无奈提供的SDK请求示例是JAVA的,而我用的是.NET的,所以还是摸了一些坑出来,其实也就是不同平 ...

  4. NLP(三) 预处理

    分词 from nltk.tokenize import LineTokenizer,SpaceTokenizer,TweetTokenizer from nltk import word_token ...

  5. 检查python标识符是否有效

  6. 洛谷P1352没有上司的舞会+树形二维DP

    传送门 题意:上司和直接下属,不能同时去一个聚会,问可邀请到的人的快乐值最大是多少: 参考:https://www.luogu.org/blog/mak2333/solution-p1352 思路: ...

  7. HDU - 3974 Assign the task (DFS建树+区间覆盖+单点查询)

    题意:一共有n名员工, n-1条关系, 每次给一个人分配任务的时候,(如果他有)给他的所有下属也分配这个任务, 下属的下属也算自己的下属, 每次查询的时候都输出这个人最新的任务(如果他有), 没有就输 ...

  8. asp.net core系列 72 Exceptionless使用介绍

    一.Exceptionless介绍 Exceptionless专注于.net平台提供实时错误和日志报告.主要包括:错误通知.智能分组异常.详细错误报告堆栈跟踪.支持离线.UI查看重要错误和确定优先级. ...

  9. 微信公众号之获取openId

    在小伙伴们开发微信公众号.小程序或者是在微信内置浏览器打开的项目时,会遇到的第一个问题就是如何获取openId,今天小编就给大家带来的是如何获取openId. 首先   我们要从微信开发者后台得到ap ...

  10. POJ 3083 Children of the Candy Corn (DFS + BFS)

    POJ-3083 题意: 给一个h*w的地图. '#'表示墙: '.'表示空地: 'S'表示起点: 'E'表示终点: 1)在地图中仅有一个'S'和一个'E',他们为位于地图的边墙,不在墙角: 2)地图 ...