c#中异步编程模型变迁及如何使用异步

大家在编程过程中都会用到一些异步编程的情况。在c#的BCL中,很多api都提供了异步方法,初学者可能对各种不同异步方法的使用感到迷惑,本文主要为大家梳理一下异步方法的变迁以及如何使用异步方法。

BeginXXX,EndXXX模式

在.Net Framework 2.0中,最常见的方法是BeginXXX,和EndXXX这样的方法来搭配使用。这种模式可以概括为方法+回调方法模式或者称为InvokeMethod+EventHandler模式。

这种模型的基本流程是:

    调用BeginXXX方法
    BeginXXX方法中传入一个回调方法,这个回调方法会在异步方法执行结束后被执行
    调用EndXXX方法,使用EndXXX方法会阻塞当前线程,直到异步方法返回结果。

我们看一个FileStream的示例方法,在.Net 2.0中,你需要这样使用异步:

using System;
using System.IO;
using System.Text;
 
public class AsyncTest
{
    public static void Main(string[] args)
    {
        using (FileStream file = new FileStream("Test.txt", FileMode.OpenOrCreate))
        {
            var bytes = Encoding.UTF8.GetBytes("Test for .net framework 2.0");
 
            IAsyncResult asyncResult = file.BeginWrite(bytes, 0, bytes.Length, callback, null);
 
            file.EndWrite(asyncResult);
        }
 
        Console.ReadLine();
    }
 
    private static void callback(IAsyncResult ar)
    {
        Console.WriteLine("Finish Write");
    }
}

XXXAsync模式

从.Net 4.0开始,微软引入了Task。由于Task本身的灵活性,也使得我们的异步编程模型更简洁。上面的例子在.Net 4.5中可以这样实现:

using System;
using System.IO;
using System.Text;
using System.Threading.Tasks;
 
public class AsyncTest
{
    public static void Main(string[] args)
    {
        using (FileStream file = new FileStream("Test.txt", FileMode.OpenOrCreate))
        {
            var bytes = Encoding.UTF8.GetBytes("Test for .net framework 4.5");
 
            var task = file.WriteAsync(bytes, 0, bytes.Length);
 
            task.Wait();
        }
 
        Console.ReadLine();
    }
}

微软在许多BCL的api中都添加了XXXAsync方法来实现新的异步模型。Task本身比回调方法灵活了许多,可以更优雅的实现回调,取消,调度等操作。关于Task的使用方式可以看我之前总结的文章link。

async和await模型

为了进一步简化异步模型,微软从Visual Studio 2012开始引入了async和await关键字。这个模型本身是基于编译器的一个语法糖,编译后会生成一个statemachine模型。这样上面例子中的写法也可以简化成:

using System;
using System.IO;
using System.Text;
using System.Threading.Tasks;
 
public class AsyncTest
{
    public static void Main(string[] args)
    {
        TestFunc();
    }
 
    private static async void TestFunc()
    {
        using (FileStream file = new FileStream("Test.txt", FileMode.OpenOrCreate))
        {
            var bytes = Encoding.UTF8.GetBytes("Test for .net framework 4.5");
            await file.WriteAsync(bytes, 0, bytes.Length);
        }
    }
}

关于异步编程模型的兼容性

如果大家注意看BCL中的类库,会发现微软并没有在最新版本的类库中对每一个BeginXXX的方法都添加了XXXAsync方法。这种情况下我们如何能让新的异步模型兼容旧的方法呢?

以NamedPipeServerStream为例,这个类库实现了一个管道的功能,微软并没有为其更新XXXAsync方法,你可以使用TaskFactory来兼容新的异步模型,你可以这样来实现:

private static void OldAsyncModel()
{
    NamedPipeServerStream pipe = new NamedPipeServerStream("customPipe", PipeDirection.InOut, -1, PipeTransmissionMode.Message, PipeOptions.Asynchronous | PipeOptions.WriteThrough);
    IAsyncResult async = pipe.BeginWaitForConnection(callback, null);
    pipe.EndWaitForConnection(async);
}
 
private static async void NewAsyncModel()
{
    NamedPipeServerStream pipe = new NamedPipeServerStream("customPipe", PipeDirection.InOut, -1, PipeTransmissionMode.Message, PipeOptions.Asynchronous | PipeOptions.WriteThrough);
 
    await Task.Factory.FromAsync(pipe.BeginWaitForConnection, pipe.EndWaitForConnection, null);
}

  

因此,我们可以总结为,.Net中有两种异步编程模型:

    不返回Task对象的调用方法+回调方法的模型
    返回Task对象的XXXAsync模型,和async,await模型

BeginXXX模型微软已经逐渐的考虑废弃,返回Task的异步编程模型目前是微软建议的方式。

c#中的异步编程模型

在读文件的时候,如果不采用异步模型,整个执行线程会挂起,直至文件读取完毕,这个时候这个线程就会一直等待直到文件读取完成为止极大的浪费资源;如果是从网络上读取文件的话,如果网络中断那么就只能抛出异常了(而线程只能忙等)。

public IAsyncResult BeginReceive(
 IList<ArraySegment<byte>> buffers,
 SocketFlags socketFlags,
 AsyncCallback callback,
 Object state)
public int EndReceive(
 IAsyncResult asyncResult)

异步模型:必然有第三,第四个参数(两者可以置为null),并返回一个IAsyncResult的类型,异步期间采用硬件驱动监听,而不需要任何线程在这里等待;
异步编程模型的三个技术要点:第四个参数是传递给回调函数的

1.采用:人工调节

BeginReceive();
...
...
EndReceive();

自己采用一个适当的时间,在EndReceive()中返回本应该在同步模型中返回的值,如此处返回int,Accept()返回一个socket,但是这导致根本没有发挥出异步的全部能力。

等待直至完成:在执行到End..函数的时候就会默认静止,从而只能节省一部分时间;

2.间歇查询

public interface IAsyncResult {
 Object AsyncState { get; }
 WaitHandle AsyncWaitHandle { get; }
 Boolean IsCompleted { get; }
 Boolean CompletedSynchronously { get; }  //不需要使用
}

线程询问:

IAsyncResult ar = fs.BeginRead(data, 0, data.Length, null, null);
while (!ar.IsCompleted) {
 Console.WriteLine("Operation not completed; still waiting.");
 Thread.Sleep(10);
}                    //直到可以确定完成了
Int32 bytesRead = fs.EndRead(ar);

类似的有:

while (!ar.AsyncWaitHandle.WaitOne(10, false)) {
Console.WriteLine("Operation not completed; still waiting.");
}
Int32 bytesRead = fs.EndRead(ar);

3.回调函数

delegate void AsyncCallback(IAsyncResult ar); 回调函数的委托(回调函数实现采用相同类型的参数,直接传入函数名)
默认将第四个object参数转换为回调函数的相应的IAsyncResult参数。
object 是一个传递给回调函数的任意类型的参数,但是要用IAsyncResult的AsyncState来显示转换
在回调函数中调用end..函数,彻底完成异步操作。(回调函数可能不是定义在一个类中)
但是在回调函数可能要和异步开始调用的函数中公用一些数据,如,写文件时候的缓冲区。

优化方法:

1.定义一个类包含一些公用的数据,然后在主函数中声明实例,然后将其做为最后一个参数传递给回调函数。(可以防止过多的静态的数据)
2.匿名委托:(解决data共享问题)

fs.BeginRead(data, 0, data.Length,
delegate(IAsyncResult ar)
{
 Int32 bytesRead = fs.EndRead(ar);    注:用什么调用begin..,就一定要用它来调用end...
 fs.Close();
 Console.WriteLine(BitConverter.ToString(data, 0, bytesRead));
},    null);

3.让每一个异步操作采用自己的一对处理函数,来达到效率的最高。

委托的异步操作模型:
internal sealed class SumDelegate : MulticastDelegate {
 public SumDelegate(Object object, IntPtr method);
 public UInt64 Invoke(UInt64 n);
 public IAsyncResult BeginInvoke(UInt64 n, AsyncCallback callback, Object object);
 public UInt64 EndInvoke(IAsyncResult result);}

时间: 2024-09-14 14:51:52

c#中异步编程模型变迁及如何使用异步的相关文章

APM之 .net中异步编程模型比较-3

.net中的异步编程模型比较 模型 主要用途 用什么模拟辅助用途 父子 进度报告 取消 等待 超时 返回结果/异常 QueueUserWorkItem 计算 同步I/O No No No No No No Timer 计算 同步I/O No No 通过Dispose No Yes No RegisterWaitForSingleObject 计算 同步I/O No No 通过Unregister No Yes Yes Tasks 计算 同步I/O TaskCompletionSource Tas

并发事件: 实现CLR异步编程模型

通常 I/O 操作的特点是速度慢.不可预见.当应用程序执行同步 I/O 操作时,基本上会放弃对正在 完成实际工作的设备的控制.例如,如果应用程序调用 StreamRead 方法从 FileStream 或 NetworkStream 读取某些字节,我们无法预计该方法需要多长时间才能返回.如果正在被读取的文件位于 本地硬盘上,那么 Read 操作可能会立即返回.如果存储文件的远程服务器脱机,则 Read 方法可能会等 待几分钟,然后超时并引发异常.在此期间,发出同步请求的线程会被占用.如果该线程是

8天玩转并行开发——第六天 异步编程模型

原文:8天玩转并行开发--第六天 异步编程模型             在.net里面异步编程模型由来已久,相信大家也知道Begin/End异步模式和事件异步模式,在task出现以后,这些东西都可以被task包装 起来,可能有人会问,这样做有什么好处,下面一一道来.   一: Begin/End模式 1: 委托     在执行委托方法的时候,我们常常会看到一个Invoke,同时也有一对你或许不常使用的BeginInvoke,EndInvoke方法对,当然Invoke方法 是阻塞主线程,而Begi

JS魔法堂:深究JS异步编程模型

前言  上周5在公司作了关于JS异步编程模型的技术分享,可能是内容太干的缘故吧,最后从大家的表情看出"这条粉肠到底在说啥?"的结果:(下面是PPT的讲义,具体的PPT和示例代码在https://github.com/fsjohnhuang/ppt/tree/master/apm_of_js上,有兴趣就上去看看吧! 重申主题  <异步编程模型>这个名称确实不太直观,其实今天我想和大家分享的就是上面的代码是如何演进成下面的代码而已. a(function(){ b(functi

重新想象 Windows 8 Store Apps (44) - 多线程之异步编程: 经典和最新的异步编程模型, IAsyncInfo 与 Task 相互转换

原文:重新想象 Windows 8 Store Apps (44) - 多线程之异步编程: 经典和最新的异步编程模型, IAsyncInfo 与 Task 相互转换 [源码下载] 重新想象 Windows 8 Store Apps (44) - 多线程之异步编程: 经典和最新的异步编程模型, IAsyncInfo 与 Task 相互转换 作者:webabcd 介绍重新想象 Windows 8 Store Apps 之 异步编程 经典的异步编程模型(IAsyncResult) 最新的异步编程模型(

浅谈.NET下的多线程和并行计算(十一).NET异步编程模型基础 下

上次我们说了,要进行多线程编程,我们可以使用最原始的方式也是最灵活的方式进行,那就是 Thread(ThreadPool)+信号量+ 锁+Control.Invoke..NET的异步编程模型给我们提供了一种基于 IAsyncResult的编程模式,它尤其适用于处理下面的应用场景: 1) 被阻止,正在等待一个 IAsyncResult 2) 被阻止,正在等待多个 IAsyncResult 对象 3) 轮询 IAsyncResult 上的完成情形 .NET还提供了基于事件的异步编程模式,它能够提供:

浅谈.NET下的多线程和并行计算(十).NET异步编程模型基础 上

谈多线程谈到现在,我们要明确多线程的一个好处是可以进行并行的运算(充分利用多核处理器,对 于桌面应用程序来说就更重要一点了,没有WEB服务器,利用多核只能靠自己),还有一个好处就是异步 操作,就是我们可以让某个长时间的操作独立运行,不妨碍主线程继续进行一些计算,然后异步的去返回 结果(也可以不返回).前者能提高性能是因为能利用到多核,而后者能提高性能是因为能让CPU不在等 待中白白浪费,其实异步从广义上来说也可以理解为某种并行的运算.在之前的这么多例子中,我们大多 采用手工方式来新开线程,之前也

一篇需要膜拜的文篇--Javascript异步编程模型进化(转)

要我能用得这么熟, 那前端出师了哈. http://foio.github.io/javascript-asyn-pattern/ 改天一个一个亲测一下. Javascript语言是单线程的,没有复杂的同步互斥:但是,这并没有限制它的使用范围:相反,借助于Node,Javascript已经在某些场景下具备通吃前后端的能力了.近几年,多线程同步IO的模式已经在和单线程异步IO的模式的对决中败下阵来,Node也因此得名.接下来我们深入介绍一下Javascript的杀手锏,异步编程的发展历程. 让我们

.NET中的异步编程“.NET技术”:使用F#简化异步编程

不管是使用yield或借助第三方类库来简化异步编程,或多或少总是感觉不那么正统,有点hack的感觉.这种感觉在实验阶段倒还可以,要是用在产品中总有点担心,即使这些类库来自权威的第三方,我不知道大家有没有跟我同样的感觉.那么这个时候我们就会想,如果在语言中直接能提供这种机制该多好呢. F#的异步工作流 在Visual Studio 2010中,新包含了一种语言:F#.F#的一大特性就是异步计算.能让你用同步的方式编写异步的代码,不用使用AsyncCallback回调将一个方法分为两段,也不用注册异