Title

通过委托实现异步调用中BeginInvoke及回调函数的使用

通过委托实现异步调用的步骤:

1.定义委托。

2.将要进行异步调用的方法“实例化”到定义的委托。

3.在委托上调用BeginInvoke方法。其中,BeginInvoke的参数由三个部分构成。第一部分:所定义的委托的函数签名。

第二部分:希望调用的回调函数的委托。第三部分:自定义委托的实例(该实例将会在回调函数中的IAsyncResult的AsyncRState属性中重构出我们在步骤2中定义的委托实例,并借助这个实例来调用EndInvoke方法。)

4.如果我们希望在当前线程来处理异步调用的结果,则可以使用BeginInvoke方法返回一个IAsyncResult实例(例如ar)

并在当前线程等待。如果我们希望在异步线程中通过回调函数来处理结果,则我们需要在3中传递一个回调委托,并在该处理中调用EndInvoke方法。

以下是一段Programming C#(4版)中的一段实例:

Code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace property
{
    public class DelegateClass
    {
        public delegate int AsyncSampDelegate();
        public event AsyncSampDelegate delEvent;

public void Run()
        {
            Console.WriteLine("The Run Thread is {0}", Thread.CurrentThread.GetHashCode());
            foreach (AsyncSampDelegate del in delEvent.GetInvocationList())
            {
                del.BeginInvoke(new AsyncCallback(ReturnAsync), del);
            }
        }

public void ReturnAsync(IAsyncResult ar)
        {
            //获得调用委托实例的引用
            AsyncSampDelegate del = (AsyncSampDelegate)ar.AsyncState;
            int result = del.EndInvoke(ar);
            Console.WriteLine("The result is {0},The Thread is {1}", result, Thread.CurrentThread.GetHashCode());
        }
    }

public class FirstSubscribe
    {
        private int myCount = 0;

public void AddFunToDel(DelegateClass tmpDel)
        {
            tmpDel.delEvent+=new DelegateClass.AsyncSampDelegate(FirstFun);
        }

public int FirstFun()
        {
            return myCount++;
        }
    }

public class SecondSubscribe
    {
        private int myCount = 0;

public void AddFunToDel(DelegateClass tmpDel)
        {
            tmpDel.delEvent+=new DelegateClass.AsyncSampDelegate(SecondFun);
        }

public int SecondFun()
        {
            return myCount += 2;
        }
    }

public class App
    {
        static void Main()
        {
            DelegateClass delClass = new DelegateClass();
            FirstSubscribe fs = new FirstSubscribe();
            SecondSubscribe ss = new SecondSubscribe();

fs.AddFunToDel(delClass);
            ss.AddFunToDel(delClass);

Console.WriteLine("The Main Thread is {0}", Thread.CurrentThread.GetHashCode());
            delClass.Run();
            Console.Read();
        }
    }
}

多线程和异步操作的异同

  多线程和异步操作两者都可以达到避免调用线程阻塞的目的,从而提高软件的可响应性。甚至有些时候我们就认为多线程和异步操作是等同的概念。但是,多线程和异步操作还是有一些区别的。而这些区别造成了使用多线程和异步操作的时机的区别。

  异步操作的本质

   所有的程序最终都会由计算机硬件来执行,所以为了更好的理解异步操作的本质,我们有必要了解一下它的硬件基础。 熟悉电脑硬件的朋友肯定对DMA这个词不陌生,硬盘、光驱的技术规格中都有明确DMA的模式指标,其实网卡、声卡、显卡也是有DMA功能的。DMA就是直 接内存访问的意思,也就是说,拥有DMA功能的硬件在和内存进行数据交换的时候可以不消耗CPU资源。只要CPU在发起数据传输时发送一个指令,硬件就开 始自己和内存交换数据,在传输完成之后硬件会触发一个中断来通知操作完成。这些无须消耗CPU时间的I/O操作正是异步操作的硬件基础。所以即使在DOS 这样的单进程(而且无线程概念)系统中也同样可以发起异步的DMA操作。

  线程的本质

  线程不是一个计算机硬件的功能,而是操作系统提供的一种逻辑功能,线程本质上是进程中一段并发运行的代码,所以线程需要操作系统投入CPU资源来运行和调度。

  异步操作的优缺点

   因为异步操作无须额外的线程负担,并且使用回调的方式进行处理,在设计良好的情况下,处理函数可以不必使用共享变量(即使无法完全不用,最起码可以减少 共享变量的数量),减少了死锁的可能。当然异步操作也并非完美无暇。编写异步操作的复杂程度较高,程序主要使用回调方式进行处理,与普通人的思维方式有些 初入,而且难以调试。

  多线程的优缺点

  多线程的优点很明显,线程中的处理程序依然是顺序执行,符合普通人的思维习惯,所以编程简单。但是多线程的缺点也同样明显,线程的使用(滥用)会给系统带来上下文切换的额外负担。并且线程间的共享变量可能造成死锁的出现。

  适用范围

   在了解了线程与异步操作各自的优缺点之后,我们可以来探讨一下线程和异步的合理用途。我认为:当需要执行I/O操作时,使用异步操作比使用线程+同步 I/O操作更合适。I/O操作不仅包括了直接的文件、网络的读写,还包括数据库操作、Web Service、HttpRequest以及.net Remoting等跨进程的调用。

  而线程的适用范围则是那种需要长时间CPU运算的场合,例如耗时较长的图形处理和算法执行。但是往 往由于使用线程编程的简单和符合习惯,所以很多朋友往往会使用线程来执行耗时较长的I/O操作。这样在只有少数几个并发操作的时候还无伤大雅,如果需要处 理大量的并发操作时就不合适了。

  实例研究

  说了那么理论上的东西,可能有些兄弟早就不耐烦了,现在我们来研究几个实际的异步操作例子吧。

  实例1:由delegate产生的异步方法到底是怎么回事?

  大家可能都知道,使用delegate可以“自动”使一个方法可以进行异步的调用。从直觉上来说,我觉得是由编译器或者CLR使用了另外的线程来执行目标方法。到底是不是这样呢?让我们来用一段代码证明一下吧。

Code
using System;
using System.Threading;

namespace AsyncDelegateDemo
{
  delegate void AsyncFoo(int i);
  class Program
  {
    /// <summary>
    /// 输出当前线程的信息
    /// </summary>
   /// <param name="name">方法名称</param>

static void PrintCurrThreadInfo(string name)
    {
      Console.WriteLine("Thread Id of " + name+ " is: " + Thread.CurrentThread.ManagedThreadId+ ", current thread is "
      + (Thread.CurrentThread.IsThreadPoolThread ? "" : "not ")
      + "thread pool thread.");
    }

/// <summary>
    /// 测试方法,Sleep一定时间
    /// </summary>
    /// <param name="i">Sleep的时间</param>
    static void Foo(int i)
    {
       PrintCurrThreadInfo("Foo()");
       Thread.Sleep(i);
    }

/// <summary>
    /// 投递一个异步调用
    /// </summary>
    static void PostAsync()
    {
      AsyncFoo caller = new AsyncFoo(Foo);
      caller.BeginInvoke(1000, new AsyncCallback(FooCallBack), caller);
    }

static void Main(string[] args)
    {
      PrintCurrThreadInfo("Main()");
      for(int i = 0; i < 10 ; i++)
      {
         PostAsync();
      }
      Console.ReadLine();
    }

static void FooCallBack(IAsyncResult ar)
    {
      PrintCurrThreadInfo("FooCallBack()");
      AsyncFoo caller = (AsyncFoo) ar.AsyncState;
      caller.EndInvoke(ar);
    }
  }
}

这段代码代码的输出如下:

Thread Id of Main() is: 1, current thread is not thread pool thread.

Thread Id of Foo() is: 3, current thread is thread pool thread.

Thread Id of FooCallBack() is: 3, current thread is thread pool thread.

Thread Id of Foo() is: 3, current thread is thread pool thread.

Thread Id of Foo() is: 4, current thread is thread pool thread.

Thread Id of Foo() is: 5, current thread is thread pool thread.

Thread Id of FooCallBack() is: 3, current thread is thread pool thread.

Thread Id of Foo() is: 3,

///

http://www.cnsdn.com.cn/blog/article.asp?id=2164

///

  。NET Framework 为异步操作提供了两种设计模式:使用 IAsyncResult 对象的异步操作与使用事件的异步操作。先来学习前者

  概述

  IAsyncResult 异步设计模式通过名为 BeginOperationName 和 EndOperationName 的两个方法来实现原同步方法的异步调用,如 FileStream 类提供了 BeginRead 和 EndRead 方法来从文件异步读取字节,它们是 Read 方法的异步版本

  Begin 方法包含同步方法签名中的任何参数,此外还包含另外两个参数:一个AsyncCallback 委托和一个用户定义的状态对象。委托用来调用回调方法,状态对象是用来向回调方法传递状态信息。该方法返回一个实现 IAsyncResult 接口的对象

  End 方法用于结束异步操作并返回结果,因此包含同步方法签名中的 ref 和 out 参数,返回值类型也与同步方法相同。该方法还包括一个 IAsyncResult 参数,用于获取异步操作是否完成的信息,当然在使用时就必须传入对应的 Begin 方法返回的对象实例

  开始异步操作后如果要阻止应用程序,可以直接调用 End 方法,这会阻止应用程序直到异步操作完成后再继续执行。也可以使用 IAsyncResult 的 AsyncWaitHandle 属性,调用其中的WaitOne等方法来阻塞线程。这两种方法的区别不大,只是前者必须一直等待而后者可以设置等待超时

  如果不阻止应用程序,则可以通过轮循 IAsyncResult 的 IsCompleted 状态来判断操作是否完成,或使用 AsyncCallback 委托来结束异步操作。AsyncCallback 委托包含一个 IAsyncResult 的签名,回调方法内部再调用 End 方法来获取操作执行结果

  尝试

  先来熟悉一下今天的主角,IAsyncResult 接口

public interface IAsyncResult
{
object AsyncState { get; }
WaitHandle AsyncWaitHandle { get; }
bool CompletedSynchronously { get; }
bool IsCompleted { get; }
}

  我用一个 AsyncDemo 类作为异步方法的提供者,后面的程序都会调用它。内部很简单,构造函数接收一个字符串作为 name ,Run 方法输出 "My name is " + name ,而异步方法直接用委托的 BeginInvoke 和 EndInvoke 方法实现

public class AsyncDemo
{
// Use in asynchronous methods
private delegate string runDelegate();
private string m_Name;
private runDelegate m_Delegate;
public AsyncDemo(string name)
{
m_Name = name;
m_Delegate = new runDelegate(Run);
}
/**
/// Synchronous method
///
///
public string Run()
{
return "My name is " + m_Name;
}
/**
/// Asynchronous begin method
///
///
///
///
public IAsyncResult BeginRun(AsyncCallback callBack, Object stateObject)
{
try
{
return m_Delegate.BeginInvoke(callBack, stateObject);
}
catch(Exception e)
{
// Hide inside method invoking stack
throw e;
}
}
/**
/// Asynchronous end method
///
///
///
public string EndRun(IAsyncResult ar)
{
if (ar == null)
throw new NullReferenceException("Arggument ar can't be null");
try
{
return m_Delegate.EndInvoke(ar);
}
catch (Exception e)
{
// Hide inside method invoking stack
throw e;
}
}
            }

  首先是 Begin 之后直接调用 End 方法,当然中间也可以做其他的操作

class AsyncTest
{
static void Main(string[] args)
{
AsyncDemo demo = new AsyncDemo("jiangnii");
// Execute begin method
IAsyncResult ar = demo.BeginRun(null, null);
// You can do other things here
// Use end method to block thread until the operation is complete
string demoName = demo.EndRun(ar);
Console.WriteLine(demoName);
}
            }

  也可以用 IAsyncResult 的 AsyncWaitHandle 属性,我在这里设置为1秒超时

class AsyncTest
{
static void Main(string[] args)
{
AsyncDemo demo = new AsyncDemo("jiangnii");
// Execute begin method
IAsyncResult ar = demo.BeginRun(null, null);
// You can do other things here
// Use AsyncWaitHandle.WaitOne method to block thread for 1 second at most
ar.AsyncWaitHandle.WaitOne(1000, false);
if (ar.IsCompleted)
{
// Still need use end method to get result, 
// but this time it will return immediately
string demoName = demo.EndRun(ar);
Console.WriteLine(demoName);
}
else
{
Console.WriteLine("Sorry, can't get demoName, the time is over");
}
}
            }

  不中断的轮循,每次循环输出一个 "."

class AsyncTest
{
static void Main(string[] args)
{
AsyncDemo demo = new AsyncDemo("jiangnii");
// Execute begin method
IAsyncResult ar = demo.BeginRun(null, null);
Console.Write("Waiting..");
while (!ar.IsCompleted)
{
Console.Write(".");
// You can do other things here
}
Console.WriteLine();
// Still need use end method to get result, 
// but this time it will return immediately
string demoName = demo.EndRun(ar);
Console.WriteLine(demoName);
}
            }

  最后是使用回调方法并加上状态对象,状态对象被作为 IAsyncResult 参数的 AsyncState 属性被传给回调方法。回调方法执行前不能让主线程退出,我这里只是简单的让其休眠了1秒。另一个与之前不同的地方是 AsyncDemo 对象被定义成了类的静态字段,以便回调方法使用

class AsyncTest
{
static AsyncDemo demo = new AsyncDemo("jiangnii");
static void Main(string[] args)
{
// State object
bool state = false;
// Execute begin method
IAsyncResult ar = demo.BeginRun(new AsyncCallback(outPut), state);
// You can do other thins here
// Wait until callback finished
System.Threading.Thread.Sleep(1000);
}
// Callback method
static void outPut(IAsyncResult ar)
{
bool state = (bool)ar.AsyncState;
string demoName = demo.EndRun(ar);
if (state)
{
Console.WriteLine(demoName);
}
else
{
Console.WriteLine(demoName + ", isn't it?");
}
}
            }

  其他

  对于一个已经实现了 BeginOperationName 和 EndOperationName 方法的对象,我们可以直接用上述方式调用,但对于只有同步方法的对象,我们要对其进行异步调用也不需要增加对应的异步方法,而只需定义一个委托并使用其 BeginInvoke 和 EndInvoke 方法就可以了

转载于:https://www.cnblogs.com/ruyi/archive/2009/07/14/1523510.html

[转] C#异步操作相关推荐

  1. C++多线程:异步操作std::async和std::promise

    文章目录 std::async 简介 使用案例 std::promise 简介 成员函数 总结 之前的文章中提到了C++多线程中的异步操作机制 C++ 多线程:future 异步访问类(线程之间安全便 ...

  2. Redux 入门教程(二):中间件与异步操作

    上一篇文章,我介绍了 Redux 的基本做法:用户发出 Action,Reducer 函数算出新的 State,View 重新渲染. 但是,一个关键问题没有解决:异步操作怎么办?Action 发出以后 ...

  3. 现在无法开始异步操作。异步操作只能在异步处理程序或模块中开始,或在页生存期中的特定事件过程中开始...

    异常处理汇总-后端系列 http://www.cnblogs.com/dunitian/p/4523006.html 这篇没啥技术含量,用来小记一番 错误信息 "System.Invalid ...

  4. 【ES6】异步操作和async函数

    [ES6]异步操作和async函数 一.基本概念 二.回调函数 三.Promise 四.async函数 查看更多ES6教学文章: 参考文献 引言:ES6新增的Generato.Promise.asyn ...

  5. 前端调用mysql异步_PHP 使用 Swoole – TaskWorker 实现异步操作 Mysql

    在一般的 Server 程序中都会有一些耗时的任务,比如:发送邮件.聊天服务器发送广播等.如果我们采用同步阻塞的防水去执行这些任务,那么这肯定会非常的慢. Swoole 的 TaskWorker 进程 ...

  6. WCF 4.0 进阶系列 – 第十二章 实现单向操作和异步操作(中)

    单向操作特别适用于"触发然后忘记"场景,在该场景中,客户端程序并不期望服务回传任何信息.但是,许多操作并不适用于这种情况,其向客户端程序返回数据.为了处理这些情况,WCF支持异步操 ...

  7. WCF 4.0 进阶系列 – 第十二章 实现单向操作和异步操作(下)

    使用消息队列 消息队列是本书WCF异步技术中的最后一个出场的.消息队列可以为消息传输提供持久性.可靠性和事务性.甚至,发送消息的客户端程序与接受消息的服务可以不必同时运行.但使用该灵活性需要付出一定的 ...

  8. 利用async和await异步操作解决node.js里面fs模块异步读写,同步结果的问题

    async await 解决异步问题,这两个关键字是es7提出的,所以测试,node和浏览器版本提高一些 async await 操作基于promise实现的 async await这两个关键字是一起 ...

  9. 【异步编程】Part3:取消异步操作

    在.Net和C#中运行异步代码相当简单,因为我们有时候需要取消正在进行的异步操作,通过本文,可以掌握 通过CancellationToken取消任务(包括non-cancellable任务).  早期 ...

  10. GreenDao自带异步操作类简析

    AsyncSession: GreenDao提供一个异步操作的统一接口类AsyncSession,它提供了你所需要的所有异步操作方法. 你可以通过调用DaoSession#startAsyncSess ...

最新文章

  1. 【PAT乙级】1072 开学寄语 (20 分)
  2. 买游戏来运营_「笔吧评测室」双十一快来了,买游戏本要做好心理准备
  3. Vuex与登录状态保存
  4. Front End Accessibility Development Guide
  5. HTML表格修改字段,HTML表格 – 更改列中单个单元格的宽度
  6. Atititcmd cli环境变量的调用设置与使用
  7. 打印准考证服务器异常显示,2020准考证打印30个常见问题汇总及解决办法
  8. Axure模板库(2)-注册登录
  9. 易语言雷电模拟器adb模块制作实现一键模拟器多开
  10. 高速内部总线HSIB和设备总线DB
  11. Win10 触摸屏 快捷键操作
  12. python压缩包怎么打开-详解python解压压缩包的五种方法
  13. 三位数的茎叶图怎么看_如何看懂茎叶图
  14. 关于北美信号T1和欧洲信号E1的计算
  15. CUDA的Occupancy和Achieved Occupancy概念
  16. LINUX驱动、系统底层
  17. socket技术路线_呐,这不就是你要的C++后台开发学习路线吗?
  18. Android利用Cookie实现码源登录效果
  19. mysql在线主从复制_一篇搞懂MySQL 8.0 Clone技术在线搭建主从复制全过程
  20. 【雅思阅读】王希伟阅读P1(阅读判断题)

热门文章

  1. javascript --- XMLHttp2级、CORS(跨域资源共享)
  2. Scrum方法论(四)
  3. 锡山国土推行数据中心“在线变更”
  4. 生活大爆炸版石头剪刀布
  5. linux下Bash编程until语句及格式化硬盘分区等编写脚本(十)
  6. LESS CSS 框架简介(转)
  7. SQL Server Insert 操作效率(堆表 VS 聚集索引表)
  8. 仅用 []()+! 就足以实现几乎任意Javascript代码
  9. 安防监控产业链全景梳理
  10. 一段H264数据的分析