浅析C#中的Thread ThreadPool Task和async/await
.net 项目中不可避免地要与线程打交道,目的都是实现异步、并发。从最开始的new Thread()入门,到后来的Task.Run(),如今在使用async/await的时候却有很多疑问。
先来看一段代码:使用Task实现异步
Task.Run(() =>
{
message = (IBytesMessage)consumer.Receive(m_Interval);
});
Receive()方法是一个延迟返回的方法,m_Interval是超时时间。如果采用同步方式执行Receive()的话,那整个程序就会被这个方法堵塞。我个人最习惯的处理方式就用Task.Run()。可惜项目要求必须使用.net framework3.5,所以只能退而求其次,放弃Task,使用Thread或者ThreadPool。
使用Thread实现异步:
new Thread(() =>
{
message = (IBytesMessage)consumer.Receive(m_Interval);
}).Start();
直接new Thread().start()这个写法是很危险的,这里只做参考。在C# 5以后的书籍中,你可能会看到这样一句话:一旦你输入了new Thread(),那就糟糕了,说明项目的代码太过时了。
使用ThreadPool实现异步:
ThreadPool.QueueUserWorkItem(Listen);
private void Listen(object state)
{
message = (IBytesMessage)consumer.Receive(m_Interval);
}
ThreadPool 内部有一套完整的线程管理机制,可以让开发者完全忽略Thread的生命周期控制。但ThreadPool中的线程,都是后台线程,当主线程执行完毕时,程序并不会等待后台线程的执行,而是直接退出。Thread则是前台线程,主程序会等待所有前台线程执行完毕后才会退出。另外在使用ThreadPool的时候需要注意QueueUserWorkItem的参数类型是:
public delegate void WaitCallback(object state) 所以,Listen方法有一个未用到的参数state。
综上,Task还是最优的解决方案。
说到这,问题看似解决了,.net 4.0及以上 Task是不二之选,低版本择优先选择ThreadPool,特殊情况考虑Thread。那么 .net4.5的新特性 async/await 有什么用呢?上述情况需要用到async/await 吗?
这里我们需要看一下完整的代码
private void Listen(object state)
{
message = (IBytesMessage)consumer.Receive(m_Interval);
if (message != null)
{
m_IAsyncMesssgae.OutputMessage(message.ToString());
}
else
{
m_IAsyncMesssgae.OutputException(new Exception("Wait timed out."));
}
}
public void OnStartAsync()
{
try
{
if (m_IsConnected && !m_IsListening)
{
connectionWPM?.Start();
m_IsListening = true;
ThreadPool.QueueUserWorkItem(Listen);
}
}
catch (Exception ex)
{
m_IAsyncMesssgae.OutputException(ex);
}
finally
{
OnStop();
}
}
这里红色字体的m_IAsyncMesssgae是一个回调的接口实例,也就说,此代码中,通过接口回调的方式把Receive()方法延迟返回的message返回给调用者。目前的代码是可以满足需求的。
我们试着用asynv和await实现一下这个需求。
public async void OnStartAsync()
{
if (m_IsConnected && !m_IsListening)
{
connectionWPM?.Start();
m_IsListening = true;
message = await Task.Run(()=> {return (IBytesMessage)consumer.Receive(m_Interval); });
}
}
1)async/await 和刚才说的Thread Task ThreadPool并不是一个概念。前者是控制异步和并发的关键字,后者是对线程的三种实现方式。
2)async/await只能和Task结合使用,async标记的方法 只能有三种返回值Task,Task<T>,void(不建议,因为async/await 就是为了获取异步方法的返回值)。
3)await等待的内容也必须是Task或者Task<T> 上面代码隐藏了一个内容,其实Task.Run()也是一个返回值为Task<T>的方法。

4)await还有一个作用是将Task<T>转成T。
5)在同一个用async标记的方法内,所有在await代码段之后的代码 都要等待await后的内容执行完成后才能执行。
6)如果一个非async方法 调用async方法获取异步返回值,那么就无法成功获取异步返回值。
再把返回值void修改一下:
public async Task<IBytesMessage> OnStartAsync()
{
if (m_IsConnected && !m_IsListening)
{
connectionWPM?.Start();
m_IsListening = true;
message = await Task.Run(()=> {return (IBytesMessage)consumer.Receive(m_Interval); });
}
return message;
}
这样一来,外部调用时候,就不需要接口回调了,直接调用OnStartAsync就可以了。切记!调用OnStartAsync的方法必须也是async,否则就直接返回message的默认值,而不是等待TaskRun()的执行。await只在所属的async方法内奏效。
调用OnStartAsync也有种不同的写法:
//写法1
async Task Handle()
{
string re = await OnStartAsync();
//dosth
}
//写法2
async Task Handle()
{
var re = OnStartAsync();
//dosth
do(await re);
}
//写法3
void Handle()
{
var re = OnStartAsync();
do(re);
}
写法1:dosth需要等待 OnStartAsync执行完毕后再执行。
写法2:dosth先执行,然后再执行do(await re)
写法3:根本就无法获取正确的返回值,实则没有等待异步执行,而是直接返回了。
以上,水平有限,如有不足,敬请指正。如有侵权 请联系作者删除。
浅析C#中的Thread ThreadPool Task和async/await的更多相关文章
- 从Thread,ThreadPool,Task, 到async await 的基本使用方法解读
记得很久以前的一个面试场景: 面试官:说说你对JavaScript闭包的理解吧? 我:嗯,平时都是前端工程师在写JS,我们一般只管写后端代码. 面试官:你是后端程序员啊,好吧,那问问你多线程编程的问题 ...
- Thread,ThreadPool,Task, 到async await 的基本使用方法和理解
很久以前的一个面试场景: 面试官:说说你对JavaScript闭包的理解吧? 我:嗯,平时都是前端工程师在写JS,我们一般只管写后端代码. 面试官:你是后端程序员啊,好吧,那问问你多线程编程的问题吧. ...
- C#中 Thread,Task,Async/Await,IAsyncResult 的那些事儿!
说起异步,Thread,Task,async/await,IAsyncResult 这些东西肯定是绕不开的,今天就来依次聊聊他们 1.线程(Thread) 多线程的意义在于一个应用程序中,有多个执行部 ...
- C#中 Thread,Task,Async/Await,IAsyncResult 的那些事儿![转载]
说起异步,Thread,Task,async/await,IAsyncResult 这些东西肯定是绕不开的,今天就来依次聊聊他们 1.线程(Thread) 多线程的意义在于一个应用程序中,有多个执行部 ...
- 详解C#中 Thread,Task,Async/Await,IAsyncResult的那些事儿
说起异步,Thread,Task,async/await,IAsyncResult 这些东西肯定是绕不开的,今天就来依次聊聊他们 1.线程(Thread) 多线程的意义在于一个应用程序中,有多个执行部 ...
- C#中 Thread,Task,Async/Await 异步编程
什么是异步 同步和异步主要用于修饰方法.当一个方法被调用时,调用者需要等待该方法执行完毕并返回才能继续执行,我们称这个方法是同步方法:当一个方法被调用时立即返回,并获取一个线程执行该方法内部的业务,调 ...
- Thread,Task,async/await,IAsyncResult
1.线程(Thread) 多线程的意义在于一个应用程序中,有多个执行部分可以同时执行:对于比较耗时的操作(例如io,数据库操作),或者等待响应(如WCF通信)的操作,可以单独开启后台线程来执行,这样主 ...
- thread、Task、async & await
学习 Jesse 的文章 async & await 的前世今生(Updated) 而来 Thread是最开始使用的多线程.new一个Thread对象,将方法传进去.手动Start() 还可以 ...
- c#中@标志的作用 C#通过序列化实现深表复制 细说并发编程-TPL 大数据量下DataTable To List效率对比 【转载】C#工具类:实现文件操作File的工具类 异步多线程 Async .net 多线程 Thread ThreadPool Task .Net 反射学习
c#中@标志的作用 参考微软官方文档-特殊字符@,地址 https://docs.microsoft.com/zh-cn/dotnet/csharp/language-reference/toke ...
随机推荐
- dubbo学习(zz)
dubbo学习 博客分类: 开源软件 Dubbo是阿里巴巴SOA服务化治理方案的核心框架,每天为2,000+个服务提供3,000,000,000+次访问量支持,并被广泛应用于阿里巴巴集团的各成员站 ...
- Linux学习笔记:JDK安装
系统为CentOS 7,安装Oracle jdk1.8 卸载系统自带的Openjdk(若无可跳过这一步,可以通过 java -version 查看),步骤是:查找Openjdk,卸载Openjdk r ...
- 【技术课堂】如何管理MongoDB数据库?
- Windows事件日志报表 怎样备份数据库?
- python生成器初步了解
一.生成器 生成器的本质就是迭代器 一个一个的创建对象 1.创建生成器的方式: 1.生成器函数 2.通过生成器表达式来获取生成器 3.类型转换 2.优点 节省内存 ,生成器本身就是代码 ...
- 840. Magic Squares In Grid
class Solution { public: int numMagicSquaresInside(vector<vector<int>>& grid) { ; in ...
- Our Future
The world is betting on how to win the football game: But I'm betting on how to win your heart: Mayb ...
- Codeforces 1098 简要题解
文章目录 前言 A题 B题 C题 D题 E题 传送门 前言 没错因为蒟蒻太菜了这场的最后一道题也咕掉了,只有AAA至EEE的题解233 A题 传送门 题意简述:给出一棵带点权的树,根节点深度为111, ...
- TCP/IP协议(3):数据链路层
OSI数据链路层上的协议有Ethernet/IEEE802.3/IEEE802.4/IEEE802.5. ARP.RARP等. 1.Ethernet(以太网) 链路层支持很多协议,比如Ethernet ...
- IntelliJ IDEA 2017版 spring-boot2.0.4+mybatis 自动部署的细节问题
一.加载pom依赖包 <!--spring-boot开发热部署--> <dependency> <groupId>org.springframework.boot& ...