# C# 中的Task创建指南
本文还处于草稿阶段,难免还有错误修改改正,逻辑还不是很清晰,笔者会努力完善,长期更新!
[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 接口提供异步行为的旧模型。 在这种模式下,同步操作需要
Begin
和End
方法(例如,BeginWrite
和EndWrite
以实现异步写入操作)。 不建议新的开发使用此模式。 有关详细信息,请参阅异步编程模型 (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 将公开 BeginRead
和 EndRead
方法:
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 的对象?
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创建指南的更多相关文章
- 【手记】MTK之TASK创建及使用
首先来看看task的数据类型声明,在config\include\hal\task_config.h中对task和module类型进行了定义. /*************************** ...
- Netty中NioEventLoopGroup的创建源码分析
NioEventLoopGroup的无参构造: public NioEventLoopGroup() { this(0); } 调用了单参的构造: public NioEventLoopGroup(i ...
- Pycharm在Ubuntu14.04中的基本使用指南
前几天给大家分享了:如何在VMware虚拟机中安装Ubuntu14.04系统.今天给大家分享一下在Ubuntu14.04中如何简单的使用Pycharm.1.启动Pycharm,将进入Pycharm的启 ...
- 深入理解gradle中的task
目录 简介 定义task tasks 集合类 Task 之间的依赖 定义task之间的顺序 给task一些描述 task的条件执行 task rule Finalizer tasks 总结 深入理解g ...
- 一、neo4j中文文档-入门指南
目录 neo4j中文文档-入门指南 Neo4j v4.4 neo4j **Cypher ** 开始使用 Neo4j 1. 安装 Neo4j 2. 文档 图数据库概念 1. 示例图 2.节点 3. 节点 ...
- JVM中对象的创建过程
JVM中对象的创建过程如以下流程图中所示: 对其主要步骤进行详细阐述: 为新生对象分配内存: 内存的分配方式: 指针碰撞:假设Java堆中内存是绝对规整的,所有用过的内存放在一边,空闲的内存在另一边, ...
- C/C++中动态链接库的创建和调用
DLL 有助于共享数据和资源.多个应用程序可同时访问内存中单个DLL 副本的内容.DLL 是一个包含可由多个程序同时使用的代码和数据的库.下面为你介绍C/C++中动态链接库的创建和调用. 动态连接库的 ...
- ora-01658 :无法为表空间USERS 中的段创建INITIAL区
"CREATE INDEX "IDX_TS_BONUS_Q_201209_DS" ON "TS_BONUS_Q_201209" ("DS&q ...
- (转).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 ...
随机推荐
- MySQL数据库笔记六:数据定义语言及数据库的备份和修复
1. MySQL中的函数 <1>加密函数 password(str) 该函数可以对字符串str进行加密,一般情况下,此函数给用户密码加密. select PASSWORD('tlxy666 ...
- [Python] Scrapy爬虫框架入门
说明: 本文主要学习Scrapy框架入门,介绍如何使用Scrapy框架爬取页面信息. 项目案例:爬取腾讯招聘页面 https://hr.tencent.com/position.php?&st ...
- canvas 的基本使用
一.canvas的介绍 canvas是html5出现的新标签,像所有的DOM对象一样它有自己本身 的属性.方法和事件,其中就有绘图的方法,js能够调用它来进行绘图.canvas只有两个属性,而且是可选 ...
- (五十三)c#Winform自定义控件-滚动文字
前提 入行已经7,8年了,一直想做一套漂亮点的自定义控件,于是就有了本系列文章. GitHub:https://github.com/kwwwvagaa/NetWinformControl 码云:ht ...
- UVA 11294 wedding 2-sat
可以把一对夫妇当成一个节点,然后拆点的话,h和w分别为真和假,然后直接按照题目中说的建图染色即可 #include <iostream> #include <cstdio> # ...
- 用深度学习技术FCN自动生成口红
1 这个是什么? 基于全卷积神经网络(FCN)的自动生成口红Python程序. 图1 FCN生成口红的效果(注:此两张人脸图来自人脸公开数据库LFW) 2 怎么使用了? 首 ...
- SecureCRT安装及破解
### SecureCRT简介 > SecureCRT是一款支持SSH(SSH1和SSH2)的终端仿真程序,简单地说是Windows下登录UNIX或Linux服务器主机的软件. > &g ...
- spring boot使用guava缓存
1.pom中插入依赖: <!--guava缓存cache--> <dependency> <groupId>com.google.guava</groupId ...
- Container容器crontab错误问题
问题描述 容器中的cron定时计划任务不执行 问题分析 排查一:检查Container容器是否安装cron # rpm -qa | grep cron # ls /etc/init.d/ 若没有安装, ...
- Ajax发送请求的四个步骤
1.创建XMLHttpRequest let xhr=new XMLHttpRequest; 2.连接服务器 xhr.open("get","goods.json&quo ...