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

[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. Leetcode之回溯法专题-77. 组合(Combinations)

    Leetcode之回溯法专题-77. 组合(Combinations)   给定两个整数 n 和 k,返回 1 ... n 中所有可能的 k 个数的组合. 示例: 输入: n = 4, k = 2 输 ...

  2. Foxmail管理多个邮箱

    使用Foxmail管理邮箱还是很方便的. 1. 下载Foxmail. 2. 双击,输入想关联的邮箱名称和密码,收取邮件即可. 3. 如果想关联多个账号,可点击Foxmail右上角的菜单栏,选择账户管理 ...

  3. codeforces 821 D. Okabe and City(最短路)

    题目链接:http://codeforces.com/contest/821/problem/D 题意:n*m地图,有k个位置是点亮的,有4个移动方向,每次可以移动到相邻的点亮位置,每次站在初始被点亮 ...

  4. poj 3660Cow Contest

    题目链接:http://poj.org/problem?id=3660 有n头奶牛还有m种关系a,b表示a牛逼b彩笔,所以a排名比b高 最后问你给出的关系最多能确定多少头奶牛的排名,而且给出的数据不会 ...

  5. 不权威的国产CPU发展历程

    最近进行了一些国产化相关工作 趁着周末有时间,自己整理一下这段时间的学习内容. 毕竟不是处理器和芯片的业内人士,里面多有纰漏,请谅解. 希望可以作为入门学习的简单知识. 1.0 远古时代 unix 世 ...

  6. Oracle数据库之七 多表查询

    七.多表查询 ​ 对于查询在之前已经学过了简单查询.限定查询.查询排序,这些都属于 SQL 的标准语句,而上一章的单行函数,主要功能是为了弥补查询的不足. ​ 而从多表查询开始就正式进入到了复杂查询部 ...

  7. 大多数人不知道的:HashMap链表成环的原因和解决方案

    引导语 在 JDK7 版本下,很多人都知道 HashMap 会有链表成环的问题,但大多数人只知道,是多线程引起的,至于具体细节的原因,和 JDK8 中如何解决这个问题,很少有人说的清楚,百度也几乎看不 ...

  8. Visual Studio 调试 —— 附加到进程(远程服务器)

    一般在部署环境下不会有 Visual Studio 开发工具的,所以需要有 Remote Debugger(远程调试器) 才可以进行远程调试. Remote Debugger 获取 方法一:Visua ...

  9. Go语言基础之文件操作

    本文主要介绍了Go语言中文件读写的相关操作. 文件是什么? 计算机中的文件是存储在外部介质(通常是磁盘)上的数据集合,文件分为文本文件和二进制文件. 打开和关闭文件 os.Open()函数能够打开一个 ...

  10. springcloud(四):应用配置中心config的安全设置

    springcloud应用配置中心config的安全设置 在springcloud应用开发中,为了方便在线管理我们的配置文件,通常会配一个配置中心config-server,这里托管着应用的一些配置文 ...