C#异步编程一
前几天把Code First系列总结完,想着下步总结什么,原本想着XML,不过XML的内容比较多,还有3天班就中秋节了,想在中秋节前在完成一个系列,所以决定把异步这块总结下。说起异步可能会认为就是多线程,其实并不是这样,多线程之一解决异步的一种方式,单线程也可以实现异步。C#中异步主要包括异步委托和异步任务。今天总结下异步委托。
一、同步问题
大家小时候都可能听说过小明上学的故事,小明起床要洗脸、烧水、做饭、跑步、吃饭,今天来的例子也还是小明,不过不是上学而是睡觉。小明睡觉之前要洗澡洗干净了。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks; namespace AsyncDemo
{
public class Heater
{
/// <summary>
/// 设定的温度
/// </summary>
public int SetTemp { get; set; } /// <summary>
/// 当前水温
/// </summary>
private int _currentTemp;
public int CurrentTemp
{
get { return _currentTemp; }
} private bool _flag;
public bool Flag
{
get { return _flag; } } public Heater()
{
this._flag = false;
} /// <summary>
/// 烧水
/// </summary>
public int BoilWater()
{ for (int i = ; i <= ; i++)
{
Thread.Sleep();
_currentTemp = i;
Console.WriteLine("当前温度{0}",_currentTemp);
if (_currentTemp >= SetTemp)
{
_flag = true;
break;
}
}
return _currentTemp;
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks; namespace AsyncDemo
{
public class XiaoMing
{
public Heater heater { get; set; } public XiaoMing(Heater heater)
{
this.heater = heater;
} public int OpenHeater()
{
Console.WriteLine("打开热水器,开始烧水");
return heater.BoilWater();
} public void WatchTv()
{
Console.WriteLine("看电视中");
}
}
}
static void Main(string[] args)
{
Heater heater = new Heater();
heater.SetTemp = ;
XiaoMing xiaoMing = new XiaoMing(heater);
xiaoMing.OpenHeater();
xiaoMing.WatchTv();
if (xiaoMing.heater.Flag)
{
Console.WriteLine("水烧好了");
}
Console.ReadLine();
}
小明想着烧着水闲着也是闲着,不能开着热水器吧,他想看电视,看片。但是看下面的输入让小明失望了。

小明傻眼了,我这要看着烧水器的温度,烧好了,才能看电视,这不行啊,还要上床呢。看电视只是在烧水的时候看会。这要是在烧水的时候我能看会电视多好。
二、异步的引入
上面也看到了,烧水和看电视不能同时进行,那怎么能同时进行,烧水又不是小明烧,是热水器烧,自己只是打开热水器就好了。为了解决这个问题,C#中有了异步委托。
首先引用MSDN中的一段话来描述一下如何使用异步方式
.NET Framework 允许您异步调用任何方法。 为此,应定义与您要调用的方法具有相同签名的委托;公共语言运行时会自动使用适当的签名为该委托定义 BeginInvoke 和 EndInvoke 方法。
BeginInvoke 方法启动异步调用。 该方法与您需要异步执行的方法具有相同的参数,还有另外两个可选参数。 第一个参数是一个 AsyncCallback 委托,该委托引用在异步调用完成时要调用的方法。 第二个参数是一个用户定义的对象,该对象将信息传递到回调方法。 BeginInvoke 立即返回,不等待异步调用完成。 BeginInvoke 返回一个 IAsyncResult,后者可用于监视异步调用的进度。
EndInvoke 方法检索异步调用的结果。 在调用 BeginInvoke 之后随时可以调用该方法。 如果异步调用尚未完成,则 EndInvoke 会一直阻止调用线程,直到异步调用完成。 EndInvoke 的参数包括您需要异步执行的方法的 out 和 ref 参数(在 Visual Basic 中为 <Out> ByRef 和 ByRef)以及由 BeginInvoke 返回的 IAsyncResult。
如果上面的定义认真多读几遍,基本把异步委托精髓学到了。下面就在上面的代码基础上改变一下,让小明可以在烧水的时候可以看电视。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks; namespace AsyncDemo
{
public class XiaoMing
{
public Heater heater { get; set; } //应定义与您要调用的方法具有相同签名的委托
public delegate int BoilWaterDelegate(int orgTemp); private BoilWaterDelegate _BoilWaterDelegate { get; set; } public XiaoMing(Heater heater)
{
this.heater = heater;
_BoilWaterDelegate = new BoilWaterDelegate(heater.BoilWater);
} /// <summary>
/// BeginInvoke 立即返回,不等待异步调用完成。 BeginInvoke 返回一个 IAsyncResult,后者可用于监视异步调用的进度。
/// </summary>
/// <param name="orgTemp"></param>
/// <param name="callBack"></param>
/// <param name="obj"></param>
/// <returns></returns>
public IAsyncResult BeginBoilWater(int orgTemp,AsyncCallback callBack, object obj)
{
Console.WriteLine("打开热水器,开始烧水");
try
{
//BeginInvoke 函数中 回调函数前面的参数是给委托传的参数 后面是给回调函数传的参数
// 该方法与需要异步执行的方法具有相同的参数,还有另外两个可选参数,orgTemp与需要异步执行的方法具有相同的参数
//第一个参数是一个 AsyncCallback 委托,该委托引用在异步调用完成时要调用的方法 callBack就是
//第二个参数是一个用户定义的对象,该对象将信息传递到回调方法 obj就是
return _BoilWaterDelegate.BeginInvoke(orgTemp, callBack, obj);
}
catch (Exception ex)
{ throw ex;
}
} /// <summary>
/// 烧水结束
/// </summary>
/// <param name="ar"></param>
/// <returns></returns>
public int EndBoilWater(IAsyncResult ar)
{
if (ar == null)
throw new NullReferenceException("IAsyncResult 参数不能为空");
try
{
return _BoilWaterDelegate.EndInvoke(ar);
}
catch (Exception e)
{
throw e;
}
} public void WatchTv()
{
Console.WriteLine("看电视中");
} public void Wash(IAsyncResult ir)
{
string someThing= ir.AsyncState as string;
Console.WriteLine("拿{0}洗澡",someThing);
} public void Sleep()
{
Console.WriteLine("上床睡觉");
}
}
}
为了运行上面的定义,在委托上加了一个参数设置一个起始温度,并设置了回调函数。并在回调函数Wash中使用了自定义的一个对象。注意上面的注释前面的BeginInvoke 的描述,它们是对应着的。
static void Main(string[] args)
{
Heater heater = new Heater();
heater.SetTemp = ;
XiaoMing xiaoMing = new XiaoMing(heater); //BeginInvoke 立即返回,不等待异步调用完成。
//WatchTv函数会在输出当前温度前面执行
//BeginInvoke 返回一个 IAsyncResult,后者可用于监视异步调用的进度
IAsyncResult ar = xiaoMing.BeginBoilWater(, xiaoMing.Wash, "肥皂");
xiaoMing.WatchTv(); //在调用 BeginInvoke 之后随时可以调用
// 如果异步调用尚未完成,则 EndInvoke 会一直阻止调用线程,直到异步调用完成
// 如果放在EndBoilWater之后就会在输出当前温度后执行
int currentTemp = xiaoMing.EndBoilWater(ar); Console.ReadLine();
}

可以从输出结果上看到,小明开始烧水之后就去看电视了,并且水也在烧着。
从上面可以看到BeginInvoke返回IAsyncResult接口类型,那IAsyncResult到底是什么呢?
//
// 摘要:
// 表示异步操作的状态。
[ComVisible(true)]
public interface IAsyncResult
{
//
// 摘要:
// 获取用户定义的对象,它限定或包含关于异步操作的信息。
//
// 返回结果:
// 用户定义的对象,它限定或包含关于异步操作的信息。
object AsyncState { get; }
//
// 摘要:
// 获取用于等待异步操作完成的 System.Threading.WaitHandle。
//
// 返回结果:
// 用于等待异步操作完成的 System.Threading.WaitHandle。
WaitHandle AsyncWaitHandle { get; }
//
// 摘要:
// 获取一个值,该值指示异步操作是否同步完成。
//
// 返回结果:
// 如果异步操作同步完成,则为 true;否则为 false。
bool CompletedSynchronously { get; }
//
// 摘要:
// 获取一个值,该值指示异步操作是否已完成。
//
// 返回结果:
// 如果操作完成则为 true,否则为 false。
bool IsCompleted { get; }
}
上面的属性可用于监视异步调用的进度。面下面的代码中使用while循环根据IsCompleted来判断异步是否完成
static void Main(string[] args)
{
Heater heater = new Heater();
heater.SetTemp = ;
XiaoMing xiaoMing = new XiaoMing(heater); //BeginInvoke 立即返回,不等待异步调用完成。
//WatchTv函数会在输出当前温度前面执行
//BeginInvoke 返回一个 IAsyncResult,后者可用于监视异步调用的进度
IAsyncResult ar = xiaoMing.BeginBoilWater(, xiaoMing.Wash, "肥皂");
xiaoMing.WatchTv();
int i = ;
while (!ar.IsCompleted)
{
if (ar.IsCompleted)
{
Console.WriteLine("水烧好了");
}
Console.WriteLine(i++);
} //在调用 BeginInvoke 之后随时可以调用
// 如果异步调用尚未完成,则 EndInvoke 会一直阻止调用线程,直到异步调用完成
// 如果放在EndBoilWater之后就会在输出当前温度后执行
int currentTemp = xiaoMing.EndBoilWater(ar); Console.ReadLine();
}

从输出的i的值可以看到while会一直执行直到IsCompleted=true。然后使用AsyncWaitHandle来判断。
使用 IAsyncResult.AsyncWaitHandle 属性获取 WaitHandle,使用其 WaitOne 方法阻止执行,直至 WaitHandle 收到信号,然后调用 EndInvoke。其实它就是个信号量,当使用其Waitone()方法的时候,程序就会发生阻塞,如果异步完成,Waitone就会返回true,否则返回false。当然最方便的就是我们可以在Waitone中设置一个时间来做超时处理,比如我们可以在 BeginBoilWater代码后增加ar.AsyncWaitHandle.WaitOne(1000),由于,异步方法的线程需要10000ms,主线程等待了1000ms后,认为是超时了,便会继续执行后面老王看电视的代码。
static void Main(string[] args)
{
Heater heater = new Heater();
heater.SetTemp = ;
XiaoMing xiaoMing = new XiaoMing(heater); //BeginInvoke 立即返回,不等待异步调用完成。
//WatchTv函数会在输出当前温度前面执行
//BeginInvoke 返回一个 IAsyncResult,后者可用于监视异步调用的进度
IAsyncResult ar = xiaoMing.BeginBoilWater(, xiaoMing.Wash, "肥皂");
ar.AsyncWaitHandle.WaitOne();
xiaoMing.WatchTv(); //在调用 BeginInvoke 之后随时可以调用
// 如果异步调用尚未完成,则 EndInvoke 会一直阻止调用线程,直到异步调用完成
// 如果放在EndBoilWater之后就会在输出当前温度后执行
int currentTemp = xiaoMing.EndBoilWater(ar); Console.ReadLine();
}

可以看到在WatchTv前使用AsyncWaitHandle.WaitOne()以看阻塞线程1000毫秒,可以看到看电视在异步线程开始一段时间才开始执行。假如也是使用while循环来输出,和前面的IsCompleted又不一样。
static void Main(string[] args)
{
Heater heater = new Heater();
heater.SetTemp = ;
XiaoMing xiaoMing = new XiaoMing(heater); //BeginInvoke 立即返回,不等待异步调用完成。
//WatchTv函数会在输出当前温度前面执行
//BeginInvoke 返回一个 IAsyncResult,后者可用于监视异步调用的进度
IAsyncResult ar = xiaoMing.BeginBoilWater(, xiaoMing.Wash, "肥皂");
xiaoMing.WatchTv();
int i = ;
bool flag = true;
while (flag)
{
flag = !ar.AsyncWaitHandle.WaitOne();
Console.WriteLine(i++);
} //在调用 BeginInvoke 之后随时可以调用
// 如果异步调用尚未完成,则 EndInvoke 会一直阻止调用线程,直到异步调用完成
// 如果放在EndBoilWater之后就会在输出当前温度后执行
int currentTemp = xiaoMing.EndBoilWater(ar); Console.ReadLine();
}

可以看到,输出i只执行了3次,并不像前面IsCompleted那样一直输出i,使用AsyncWaitHandle使线程阻塞,不再执行,直到信号量为true。
C#异步编程一的更多相关文章
- Task C# 多线程和异步模型 TPL模型 【C#】43. TPL基础——Task初步 22 C# 第十八章 TPL 并行编程 TPL 和传统 .NET 异步编程一 Task.Delay() 和 Thread.Sleep() 区别
Task C# 多线程和异步模型 TPL模型 Task,异步,多线程简单总结 1,如何把一个异步封装为Task异步 Task.Factory.FromAsync 对老的一些异步模型封装为Task ...
- 异步编程之Generator(1)——领略魅力
异步编程系列教程: (翻译)异步编程之Promise(1)--初见魅力 异步编程之Promise(2):探究原理 异步编程之Promise(3):拓展进阶 异步编程之Generator(1)--领略魅 ...
- 异步编程之Promise(3):拓展进阶
异步编程系列教程: (翻译)异步编程之Promise(1)--初见魅力 异步编程之Promise(2):探究原理 异步编程之Promise(3):拓展进阶 异步编程之Generator(1)--领略魅 ...
- 异步编程之Promise(2):探究原理
异步编程系列教程: (翻译)异步编程之Promise(1)--初见魅力 异步编程之Promise(2):探究原理 异步编程之Promise(3):拓展进阶 异步编程之Generator(1)--领略魅 ...
- (翻译)异步编程之Promise(1):初见魅力
原文:https://www.promisejs.org/ by Forbes Lindesay 异步编程系列教程: (翻译)异步编程之Promise(1)--初见魅力 异步编程之Promise(2) ...
- net异步编程之await
net异步编程之await 初探asp.net异步编程之await 终于毕业了,也顺利进入一家期望的旅游互联网公司.27号入职.放肆了一个多月没写代码,好方啊. 另外一下观点均主要针对于await ...
- Javascript异步编程之setTimeout与setInterval详解分析(一)
Javascript异步编程之setTimeout与setInterval 在谈到异步编程时,本人最主要会从以下三个方面来总结异步编程(注意:特别解释:是总结,本人也是菜鸟,所以总结不好的,请各位大牛 ...
- 异步编程之co——源码分析
异步编程系列教程: (翻译)异步编程之Promise(1)--初见魅力 异步编程之Promise(2):探究原理 异步编程之Promise(3):拓展进阶 异步编程之Generator(1)--领略魅 ...
- 异步编程之Generator(2)——剖析特性
异步编程系列教程: (翻译)异步编程之Promise(1)--初见魅力 异步编程之Promise(2):探究原理 异步编程之Promise(3):拓展进阶 异步编程之Generator(1)--领略魅 ...
随机推荐
- 数据库相关 sql 语句
1.操作某数据库 use 数据库名称,然后可以操作该数据库下的某张表 2.$res=mysql_query($sql); 该语句如果用在封装的函数体里,则不用传入第二个参数$conn来指定连接,这样才 ...
- Java NIO入门(二):缓冲区内部细节
Java NIO 入门(二)缓冲区内部细节 概述 本文将介绍 NIO 中两个重要的缓冲区组件:状态变量和访问方法 (accessor). 状态变量是前一文中提到的"内部统计机制"的 ...
- 设计模式C#实现(一)——模板方法模式
模板方法模式——在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中.模板方法使得子类可以在不改变算法结构的情况下,重写定义算法中的某些步骤. 假如我们有两种咖啡因饮料:茶和咖啡.茶的制作过程为: ...
- linux基本命令学习笔记
这个几天在研究linux的常用基本命令.以下是此时间内的幻灯片截图笔记,在这里留个脚印. linux 常用命令 1,命令的基本格式 2,文件处理命令 3,文件搜索命令 4,帮助命令 5,压缩解压缩命令 ...
- Linux环境安装MQ
MQ下载地址:http://www-03.ibm.com/software/products/us/en/wmq/ 安装的MQ软件包为WMQv600Trial-x86_linux_2.tar.gz. ...
- R语言画图布局摆放(layout)
require(ggplot2) require(Cairo) require(grid) p = ggplot(iris,aes(x = Species,y = Sepal.Length,colou ...
- ubuntu16.04 安装网易云音乐
最爱的播放器 网易云音乐 哈哈,刚刚折腾了双系统,立马开始了软件安装. 网易云音乐从官网下载对应的 64 位版本,我下载的是 netease-cloud-music_1.0.0_amd64_ubunt ...
- BI测试工具之跨数据库数据对比,支持oracle,sqlserver
应用场景: 本周在进行SIT,我帮助仅有的一个测试妹妹对部分表进行数据质量验证,第一步需要做的就是比对source与stage表的table definition 与 数据内容的一致性. 本项目使用的 ...
- Codeforces Round #267 Div.2 D Fedor and Essay -- 强连通 DFS
题意:给一篇文章,再给一些单词替换关系a b,表示单词a可被b替换,可多次替换,问最后把这篇文章替换后(或不替换)能达到的最小的'r'的个数是多少,如果'r'的个数相等,那么尽量是文章最短. 解法:易 ...
- 学习web前端三个月感悟
总结一下自己学习前端三个月的进步和不足: 其实也算机遇,开学时,便有一个PHP培训,只记得当时拿到培训课程的时候,第一感觉就是 好难,什么留言板制作,学生信息系统的制作,navicat和PHP结合使用 ...