深入分析C#异步编程详解

同步方法和异步方法的区别
同步方法调用在程序继续执行之前需要等待同步方法执行完毕返回结果
异步方法则在被调用之后立即返回以便程序在被调用方法完成其任务的同时执行其它操作
异步编程概览
.NET Framework 允许您异步调用任何方法。定义与您需要调用的方法具有相同签名的委托;公共语言运行库将自动为该委托定义具有适当签名
的 BeginInvoke 和 EndInvoke 方法。
BeginInvoke 方法用于启动异步调用。它与您需要异步执行的方法具有相同的参数,只不过还有两个额外的参数(将在稍后描述)。
BeginInvoke 立即返回,不等待异步调用完成。
BeginInvoke 返回 IasyncResult,可用于监视调用进度。
EndInvoke 方法用于检索异步调用结果。调用 BeginInvoke 后可随时调用 EndInvoke 方法;如果异步调用未完成,EndInvoke 将一直阻塞到
异步调用完成。EndInvoke 的参数包括您需要异步执行的方法的 out 和 ref 参数(在 Visual Basic 中为 <Out> ByRef 和 ByRef)以及由
BeginInvoke 返回的 IAsyncResult。
四种使用 BeginInvoke 和 EndInvoke 进行异步调用的常用方法。调用了 BeginInvoke 后,可以:
1.进行某些操作,然后调用 EndInvoke 一直阻塞到调用完成。
2.使用 IAsyncResult.AsyncWaitHandle 获取 WaitHandle,使用它的 WaitOne 方法将执行一直阻塞到发出 WaitHandle 信号,然后调用
EndInvoke。这里主要是主程序等待异步方法,等待异步方法的结果。
3.轮询由 BeginInvoke 返回的 IAsyncResult,IAsyncResult.IsCompeted确定异步调用何时完成,然后调用 EndInvoke。此处理个人认为与
相同。
4.将用于回调方法的委托传递给 BeginInvoke。该方法在异步调用完成后在 ThreadPool 线程上执行,它可以调用 EndInvoke。这是在强制装
换回调函数里面IAsyncResult.AsyncState(BeginInvoke方法的最后一个参数)成委托,然后用委托执行EndInvoke。
警告   始终在异步调用完成后调用 EndInvoke。
以上有不理解的稍后可以再理解。
 
例子
1)先来个简单的没有回调函数的异步方法例子
请再运行程序的时候,仔细看注释,对理解很有帮助。还有,若将注释的中的两个方法都同步,你会发现异步运行的速度优越性。


代码如下:

using System;
 namespace ConsoleApplication1
 {
     class Class1
     {
         //声明委托
         public delegate void AsyncEventHandler();

//异步方法
         void Event1()
        {
            Console.WriteLine("Event1 Start");
            System.Threading.Thread.Sleep(4000);
            Console.WriteLine("Event1 End");
        }
        // 同步方法
        void Event2()
        {
            Console.WriteLine("Event2 Start");
            int i=1;
            while(i<1000)
            {
                i=i+1;
                Console.WriteLine("Event2 "+i.ToString());
            }
            Console.WriteLine("Event2 End");
        }
        [STAThread]
        static void Main(string[] args)
        {
            long start=0;
            long end=0;
            Class1 c = new Class1();
            Console.WriteLine("ready");
            start=DateTime.Now.Ticks;
            //实例委托
            AsyncEventHandler asy = new AsyncEventHandler(c.Event1);
            //异步调用开始,没有回调函数和AsyncState,都为null
            IAsyncResult ia = asy.BeginInvoke(null, null);
            //同步开始,
            c.Event2();
            //异步结束,若没有结束,一直阻塞到调用完成,在此返回该函数的return,若有返回值。

asy.EndInvoke(ia);
            //都同步的情况。
            //c.Event1();
            //c.Event2();

end =DateTime.Now.Ticks;
            Console.WriteLine("时间刻度差="+ Convert.ToString(end-start) );
            Console.ReadLine();
        }
    }
}

2)下面看有回调函数的WebRequest和WebResponse的异步操作。


代码如下:

using System;
using System.Net;
using System.Threading;
using System.Text;
using System.IO;
// RequestState 类用于通过
// 异步调用传递数据
public class RequestState
{
    const int BUFFER_SIZE = 1024;
    public StringBuilder RequestData;
    public byte[] BufferRead;
    public HttpWebRequest Request;
    public Stream ResponseStream;
    // 创建适当编码类型的解码器
    public Decoder StreamDecode = Encoding.UTF8.GetDecoder();
    public RequestState()
    {
        BufferRead = new byte[BUFFER_SIZE];
        RequestData = new StringBuilder("");
        Request = null;
        ResponseStream = null;
    }
}
// ClientGetAsync 发出异步请求
class ClientGetAsync
{
    public static ManualResetEvent allDone = new ManualResetEvent(false);
    const int BUFFER_SIZE = 1024;
    public static void Main(string[] args)
    {
        if (args.Length < 1)
        {
            showusage();
            return;
        }
        // 从命令行获取 URI
        Uri HttpSite = new Uri(args[0]);
        // 创建请求对象
        HttpWebRequest wreq = (HttpWebRequest)WebRequest.Create(HttpSite);
        // 创建状态对象
        RequestState rs = new RequestState();
        // 将请求添加到状态,以便它可以被来回传递
        rs.Request = wreq;
        // 发出异步请求
        IAsyncResult r = (IAsyncResult)wreq.BeginGetResponse(new AsyncCallback(RespCallback), rs);
        // 将 ManualResetEvent 设置为 Wait,
        // 以便在调用回调前,应用程序不退出
        allDone.WaitOne();
    }
    public static void showusage()
    {
        Console.WriteLine("尝试获取 (GET) 一个 URL");
        Console.WriteLine("\r\n用法::");
        Console.WriteLine("ClientGetAsync URL");
        Console.WriteLine("示例::");
        Console.WriteLine("ClientGetAsync http://www.microsoft.com/net/");
    }
    private static void RespCallback(IAsyncResult ar)
    {
        // 从异步结果获取 RequestState 对象
        RequestState rs = (RequestState)ar.AsyncState;
        // 从 RequestState 获取 HttpWebRequest
        HttpWebRequest req = rs.Request;
        // 调用 EndGetResponse 生成 HttpWebResponse 对象
        // 该对象来自上面发出的请求
        HttpWebResponse resp = (HttpWebResponse)req.EndGetResponse(ar);
        // 既然我们拥有了响应,就该从
        // 响应流开始读取数据了
        Stream ResponseStream = resp.GetResponseStream();
        // 该读取操作也使用异步完成,所以我们
        // 将要以 RequestState 存储流
        rs.ResponseStream = ResponseStream;
        // 请注意,rs.BufferRead 被传入到 BeginRead。
        // 这是数据将被读入的位置。
        IAsyncResult iarRead = ResponseStream.BeginRead(rs.BufferRead, 0, BUFFER_SIZE, new AsyncCallback(ReadCallBack), rs);
    }
    private static void ReadCallBack(IAsyncResult asyncResult)
    {
        // 从 asyncresult 获取 RequestState 对象
        RequestState rs = (RequestState)asyncResult.AsyncState;
        // 取出在 RespCallback 中设置的 ResponseStream
        Stream responseStream = rs.ResponseStream;
        // 此时 rs.BufferRead 中应该有一些数据。
        // 读取操作将告诉我们那里是否有数据
        int read = responseStream.EndRead(asyncResult);
        if (read > 0)
        {
            // 准备 Char 数组缓冲区,用于向 Unicode 转换
            Char[] charBuffer = new Char[BUFFER_SIZE];
            // 将字节流转换为 Char 数组,然后转换为字符串
            // len 显示多少字符被转换为 Unicode
            int len = rs.StreamDecode.GetChars(rs.BufferRead, 0, read, charBuffer, 0);
            String str = new String(charBuffer, 0, len);
            // 将最近读取的数据追加到 RequestData stringbuilder 对象中,
            // 该对象包含在 RequestState 中
            rs.RequestData.Append(str);
            // 现在发出另一个异步调用,读取更多的数据
            // 请注意,将不断调用此过程,直到
            // responseStream.EndRead 返回 -1
            IAsyncResult ar = responseStream.BeginRead(rs.BufferRead, 0, BUFFER_SIZE, new AsyncCallback(ReadCallBack), rs);
        }
        else
        {
            if (rs.RequestData.Length > 1)
            {
                // 所有数据都已被读取,因此将其显示到控制台
                string strContent;
                strContent = rs.RequestData.ToString();
                Console.WriteLine(strContent);
            }
            // 关闭响应流
            responseStream.Close();
            // 设置 ManualResetEvent,以便主线程可以退出
            allDone.Set();
        }
        return;
    }
}

在这里有回调函数,且异步回调中又有异步操作。
首先是异步获得ResponseStream,然后异步读取数据。
这个程序非常经典。从中可以学到很多东西的。我们来共同探讨。
总结
上面说过,.net framework 可以异步调用任何方法。所以异步用处广泛。
在.net framework 类库中也有很多异步调用的方法。一般都是已Begin开头End结尾构成一对,异步委托方法,外加两个回调函数和AsyncState参数,组成异步操作的宏观体现。所以要做异步编程,不要忘了委托delegate、Begin,End,AsyncCallBack委托,AsyncState实例(在回调函数中通过IAsyncResult.AsyncState来强制转换),IAsycResult(监控异步),就足以理解异步真谛了。

(0)

相关推荐

  • C#编程总结(六)详解异步编程

    1.什么是异步? 异步操作通常用于执行完成时间可能较长的任务,如打开大文件.连接远程计算机或查询数据库.异步操作在主应用程序线程以外的线程中执行.应用程序调用方法异步执行某个操作时,应用程序可在异步方法执行其任务时继续执行. 2.同步与异步的区别 同步(Synchronous):在执行某个操作时,应用程序必须等待该操作执行完成后才能继续执行. 异步(Asynchronous):在执行某个操作时,应用程序可在异步操作执行时继续执行.实质:异步操作,启动了新的线程,主线程与方法线程并行执行. 3.异

  • C#实现异步编程的方法

    最近在我参与的几个.Net项目中都有用到异步编程,作为一名.Net小白,很有必要好好地学习一下C#异步编程. 什么是异步 异步指的就是不用阻塞当前线程来等待任务的完成,而是将任务扔到线程池中去执行,当前线程可以继续向下执行,直至其它线程将任务完成,并回调通知当前线程.整个任务从开始到结束都是异步完成的,不会阻塞当前线程.因此,异步很重要的一点就是,不会阻塞当前线程. 实现异步编程 在C#语言中,主要是通过委托来实现异步编程的,在委托类型中定义了两个方法BeginInvoke()和EndInvok

  • C#异步编程详解

    前言 本节主要介绍异步编程中Task.Async和Await的基础知识. 什么是异步? 异步处理不用阻塞当前线程来等待处理完成,而是允许后续操作,直至其它线程将处理完成,并回调通知此线程. 异步和多线程 相同点:避免调用线程阻塞,从而提高软件的可响应性. 不同点: 异步操作无须额外的线程负担,并且使用回调的方式进行处理,在设计良好的情况下,处理函数可以不必使用共享变量(即使无法完全不用,最起码可以减少 共享变量的数量),减少了死锁的可能.C#5.0 .NET4.5 以后关键字Async和Awai

  • 深入分析C#异步编程详解

    同步方法和异步方法的区别同步方法调用在程序继续执行之前需要等待同步方法执行完毕返回结果异步方法则在被调用之后立即返回以便程序在被调用方法完成其任务的同时执行其它操作异步编程概览.NET Framework 允许您异步调用任何方法.定义与您需要调用的方法具有相同签名的委托:公共语言运行库将自动为该委托定义具有适当签名的 BeginInvoke 和 EndInvoke 方法.BeginInvoke 方法用于启动异步调用.它与您需要异步执行的方法具有相同的参数,只不过还有两个额外的参数(将在稍后描述)

  • java异步编程详解

    很多时候我们都希望能够最大的利用资源,比如在进行IO操作的时候尽可能的避免同步阻塞的等待,因为这会浪费CPU的资源.如果在有可读的数据的时候能够通知程序执行读操作甚至由操作系统内核帮助我们完成数据的拷贝,这再好不过了.从NIO到CompletableFuture.Lambda.Fork/Join,java一直在努力让程序尽可能变的异步甚至拥有更高的并行度,这一点一些函数式语言做的比较好,因此java也或多或少的借鉴了某些特性.下面介绍一种非常常用的实现异步操作的方式. 考虑有一个耗时的操作,操作

  • Pythony运维入门之Socket网络编程详解

    Socket是什么? Socket 是电脑网络中进程间数据流的端点Socket 是操作系统的通信机制应用程序通过Socket进行网络数据的传输 首先,简单了解一下TCP通信过程: TCP三次握手(面试常考): 第一次握手:客户端 发送SYN报文,设置随机数序号X,服务器由SYN=1知道,客户端要求建立联机 第二次握手:服务器端接收到客户端的报文之后,经过处理,返回给客户端SYN+ACK报文,同时设置随机序号Y,此时返回的报文确认ACK=X+1 第三次握手:接收到报文的客户端,会在处理确认之后,再

  • Spring中@Async注解实现异步调详解

    异步调用 在解释异步调用之前,我们先来看同步调用的定义:同步就是整个处理过程顺序执行,当各个过程都执行完毕,并返回结果. 异步调用则是只是发送了调用的指令,调用者无需等待被调用的方法完全执行完毕,继续执行下面的流程.例如, 在某个调用中,需要顺序调用 A, B, C三个过程方法:如他们都是同步调用,则需要将他们都顺序执行完毕之后,过程才执行完毕: 如B为一个异步的调用方法,则在执行完A之后,调用B,并不等待B完成,而是执行开始调用C,待C执行完毕之后,就意味着这个过程执行完毕了. 概述说明 Sp

  • Python Socket编程详解

    背景 关于Python Socket编程,首先需要了解几个计算机网络的知识,通过以下的几个问题,有助于更好的理解Socket编程的意义,以及整个框架方面的知识: TCP和UDP协议本质上的区别? TCP协议,面向连接,可靠,基于字节流的传输层通信协议:UDP协议无连接,不可靠,基于数据包的传输层协议. TCP协议在建立连接的过程需要经历三次握手,断开连接则需要经历四次挥手,而这建立连接的过程增加了传输过程中的安全性. 而建立连接的过程则会消耗系统的资源,消耗更多的时间,而相比较UDP协议传输过程

  • Python网络编程详解

    1.服务器就是一系列硬件或软件,为一个或多个客户端(服务的用户)提供所需的"服务".它存在唯一目的就是等待客户端的请求,并响应它们(提供服务),然后等待更多请求. 2.客户端/服务器架构既可以应用于计算机硬件,也可以应用于计算机软件. 3.在服务器响应客户端之前,首先会创建一个通信节点,它能够使服务器监听请求. 一.套接字:通信端点 1.套接字 套接字是计算机网络数据结构,它体现了上节中所描述的"通信端点"的概念.在任何类型的通信开始之前,网络应用程序必须创建套接字

  • log4j2异步Logger(详解)

    1 异步Logger的意义 之前的日志框架基本都实现了AsyncAppender,被证明对性能的提升作用非常明显. 在log4j2日志框架中,增加了对Logger的异步实现.那么这一步的解耦,意义何在呢? 如图,按我目前的理解:异步Logger是让业务逻辑把日志信息放入Disruptor队列后可以直接返回(无需等待"挂载的各个Appender"都取走数据) 优点:更高吞吐.调用log方法更低的延迟. 缺点:异常处理麻烦. 可变日志消息问题.更大的CPU开销.需要等待"最慢的A

  • Python基础教程之tcp socket编程详解及简单实例

    Python tcp socket编程详解 初学脚本语言Python,测试可用的tcp通讯程序: 服务器: #!/usr/bin/env python # -*- coding: utf-8 -*- import socket import threading import time def tcplink(sock, addr): print('Accept new connection from %s:%s...' % addr); sock.send(b'Welcome!!!'); whi

  • C++ 线程(串行 并行 同步 异步)详解

    C++  线程(串行 并行 同步 异步)详解 看了很多关于这类的文章,一直没有总结.不总结的话就会一直糊里糊涂,以下描述都是自己理解的非官方语言,不一定严谨,可当作参考. 首先,进程可理解成一个可执行文件的执行过程.在ios app上的话我们可以理解为我们的app的.ipa文件执行过程也即app运行过程.杀掉app进程就杀掉了这个app在系统里运行所占的内存. 线程:线程是进程的最小单位.一个进程里至少有一个主线程.就是那个main thread.非常简单的app可能只需要一个主线程即UI线程.

随机推荐