.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的更多相关文章

  1. 从Thread,ThreadPool,Task, 到async await 的基本使用方法解读

    记得很久以前的一个面试场景: 面试官:说说你对JavaScript闭包的理解吧? 我:嗯,平时都是前端工程师在写JS,我们一般只管写后端代码. 面试官:你是后端程序员啊,好吧,那问问你多线程编程的问题 ...

  2. Thread,ThreadPool,Task, 到async await 的基本使用方法和理解

    很久以前的一个面试场景: 面试官:说说你对JavaScript闭包的理解吧? 我:嗯,平时都是前端工程师在写JS,我们一般只管写后端代码. 面试官:你是后端程序员啊,好吧,那问问你多线程编程的问题吧. ...

  3. C#中 Thread,Task,Async/Await,IAsyncResult 的那些事儿!

    说起异步,Thread,Task,async/await,IAsyncResult 这些东西肯定是绕不开的,今天就来依次聊聊他们 1.线程(Thread) 多线程的意义在于一个应用程序中,有多个执行部 ...

  4. C#中 Thread,Task,Async/Await,IAsyncResult 的那些事儿![转载]

    说起异步,Thread,Task,async/await,IAsyncResult 这些东西肯定是绕不开的,今天就来依次聊聊他们 1.线程(Thread) 多线程的意义在于一个应用程序中,有多个执行部 ...

  5. 详解C#中 Thread,Task,Async/Await,IAsyncResult的那些事儿

    说起异步,Thread,Task,async/await,IAsyncResult 这些东西肯定是绕不开的,今天就来依次聊聊他们 1.线程(Thread) 多线程的意义在于一个应用程序中,有多个执行部 ...

  6. C#中 Thread,Task,Async/Await 异步编程

    什么是异步 同步和异步主要用于修饰方法.当一个方法被调用时,调用者需要等待该方法执行完毕并返回才能继续执行,我们称这个方法是同步方法:当一个方法被调用时立即返回,并获取一个线程执行该方法内部的业务,调 ...

  7. Thread,Task,async/await,IAsyncResult

    1.线程(Thread) 多线程的意义在于一个应用程序中,有多个执行部分可以同时执行:对于比较耗时的操作(例如io,数据库操作),或者等待响应(如WCF通信)的操作,可以单独开启后台线程来执行,这样主 ...

  8. thread、Task、async & await

    学习 Jesse 的文章 async & await 的前世今生(Updated) 而来 Thread是最开始使用的多线程.new一个Thread对象,将方法传进去.手动Start() 还可以 ...

  9. 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 ...

随机推荐

  1. 企业官网原型制作分享-Starbucks

    星巴克是全球著名的咖啡连锁店,星巴克的产品不单是咖啡,咖啡只是一种载体.而正是通过咖啡这种载体,星巴克把一种独特的格调传送给顾客.咖啡的消费很大程度上是一种感性的文化层次上的消费,文化的沟通需要的就是 ...

  2. debian9安装mysql mariadb

    debian9下mysql 替换成mariadb-server-10.1 不过两者类似 具体可见 <MySQL和mariadb区别> http://ask.chinaunix.net/qu ...

  3. having 的用法

    聚合函数 where 后面不能直接使用聚合函数 处理函数 题目 编写一个 SQL 查询,查找 Person 表中所有重复的电子邮箱. 示例: +----+---------+ | Id | Email ...

  4. C# WebService创建、发布、调用的简单例子

    Web service是一个平台独立的,低耦合的,自包含的.基于可编程的web的应用程序,可使用开放的XML标准来描述.发布.发现.协调和配置这些应用程序,用于开发分布式的互操作的应用程序. Web ...

  5. Django之admin管理数据库,cookie验证及分页设置

    一.admin管理数据库 1)models.py创建class类表 class Book(models.Model): name=models.CharField(max_length=) price ...

  6. 33、iOS10 由于权限问题导致崩溃的大坑

    控制台报忠告: This app has crashed because it attempted to access privacy-sensitive data without a usage d ...

  7. yum 安装报错:Could not retrieve mirrorlist http://mirrorlist.centos.org/?release=7&arch=x86_64&repo=os&infra=stock error was 14: curl#6 - "Could not resolve host: mirrorlist.centos.org; Unknown error"

    [root@venn09 ~]# yum install -y vim Loaded plugins: fastestmirror Could not retrieve mirrorlist http ...

  8. from __future__ import

    读代码的过程中看到的,好奇搜索了一下,其实当在我们调试别人Python代码的过程中经常会遇到一些问题,比如版本不同,代码也会有所改变,比如print函数 Python 2.7版本为 print “ ” ...

  9. pat -1004(树的遍历)

    题目链接:https://pintia.cn/problem-sets/994805342720868352/problems/994805521431773184 思路: (1)用vector记录每 ...

  10. springboot深入学习(三)-----docker

    一.spring data思路 spring data使用统一的api来对各种数据库存储技术进行数据访问操作提供了支持,包括oracle.mysql.redis.mongoDB等等.主要是通过spri ...