欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

C#基础之异步调用实例教程

程序员文章站 2023-12-20 17:40:22
本文实例形式展示了c#中异步调用的实现方法,并对其原理进行了较为深入的分析,现以教程的方式分享给大家供大家参考之用。具体如下: 首先我们来看一个简单的例子: 小明在烧水...

本文实例形式展示了c#中异步调用的实现方法,并对其原理进行了较为深入的分析,现以教程的方式分享给大家供大家参考之用。具体如下:

首先我们来看一个简单的例子:

小明在烧水,等水烧开以后,将开水灌入热水瓶,然后开始整理家务
小文在烧水,在烧水的过程中整理家务,等水烧开以后,放下手中的家务活,将开水灌入热水瓶,然后继续整理家务
这也是日常生活中很常见的情形,小文的办事效率明显要高于小明。从c#程序执行的角度考虑,小明使用的同步处理方式,而小文则使用的异步处理方式。

同步处理方式下,事务是按顺序一件一件处理的;而异步方式则是,将子操作从主操作中分离出来,主操作继续进行,子操作在完成处理的时候通知主操作。

在c#中,异步通过委托来完成。请看下面的例子:

class program  
{  
  static timespan boil()  
  {  
    console.writeline("水壶:开始烧水...");  
    thread.sleep(6000);  
    console.writeline("水壶:水已经烧开了!");  
    return timespan.minvalue;  
  }  
 
  delegate timespan boilingdelegate();  
 
  static void main(string[] args)  
  {  
    console.writeline("小文:将水壶放在炉子上");  
    boilingdelegate d = new boilingdelegate(boil);  
    iasyncresult result = d.begininvoke(boilingfinishedcallback, null);  
    console.writeline("小文:开始整理家务...");  
    for (int i = 0; i < 20; i++)  
    {  
      console.writeline("小文:整理第{0}项家务...", i + 1);  
      thread.sleep(1000);  
    }  
  }  
 
  static void boilingfinishedcallback(iasyncresult result)  
  {  
    asyncresult asyncresult = (asyncresult)result;  
    boilingdelegate del = (boilingdelegate)asyncresult.asyncdelegate;
    del.endinvoke(result);
    console.writeline("小文:将热水灌到热水瓶");  
    console.writeline("小文:继续整理家务");   
  }  
}

上面的例子是一个最简单的异步调用的例子,没有对异步调用函数做任何参数传递以及返回值校验。这个例子反映了小文烧水的流程,首先小文将水壶放在炉子上,在定义好委托以后,就使用begininvoke方法开始异步调用,即让水壶开始烧水,于是小文便开始整理家务。水烧开后,c#的异步模型会触发由begininvoke方法所指定的回调函数,也就是水烧开后的处理逻辑由这个回调函数定义,此时小文将水灌入热水瓶并继续整理家务。

由此可见,在c#中实现异步调用其实并不复杂,首先创建一个异步处理函数,并针对其定义一个委托;然后在调用函数的时候,使用委托的begininvoke方法,指定在函数处理完成时的回调函数(如果不需要对完成事件做处理,可以给null值),并指定所需的参数(如果没有参数,也可以给null值);最后在回调函数中处理完成事件。

请注意上例回调函数中的endinvoke调用,endinvoke会使得调用线程阻塞,直到异步函数处理完成。显然,紧接在begininvoke后面的endinvoke使用方式与同步调用等价。

endinvoke调用的返回值也就是异步处理函数的返回值。我们把程序稍作修改,将boil方法改成下面的形式:

static timespan boil()  
{  
  datetime begin = datetime.now;  
  console.writeline("水壶:开始烧水...");  
  thread.sleep(6000);  
  console.writeline("水壶:水已经烧开了!");  
  return datetime.now - begin;  
}  

然后将boilingfinishedcallback改成下面的形式:

static void boilingfinishedcallback(iasyncresult result)  
{  
  asyncresult asyncresult = (asyncresult)result;  
  boilingdelegate del = (boilingdelegate)asyncresult.asyncdelegate;  
  console.writeline("(烧水一共用去{0}时间)", del.endinvoke(result));  
  console.writeline("小文:将热水灌到热水瓶");  
  console.writeline("小文:继续整理家务");  
}  

那么我们就可以在endinvoke的时候,获得由boil异步处理函数返回的时间值。事实上,如果定义的boilingdelegate委托存在参数列表,那么我们也可以在begininvoke的时候,将所需的参数传给异步处理函数。begininvoke/endinvoke函数的签名与定义它们的委托签名有关。

注意:在修改后的boilingfinishedcallback方法中,为了得到委托实例以便获取异步处理函数的返回值,我们采用了下面的转换:

asyncresult asyncresult = (asyncresult)result;  
boilingdelegate del = (boilingdelegate)asyncresult.asyncdelegate;  

这样才能获得调用异步处理函数的委托的实体。

.net处理异步函数调用,事实上是通过线程来完成的。这个过程有以下几个特点:

1.异步函数由线程完成,这个线程是.net线程池中的线程

2.通常情况下,.net线程池拥有500个线程(当然这个数量可以设置),每当调用begininvoke开始异步处理时,异步处理函数就由线程池中的某个线程负责执行,而用户无法控制具体是由哪个线程负责执行

3.由于线程池中线程数量有限,因此当池中线程被完全占用时,新的调用请求将使函数不得不等待空余线程的出现。此时,程序的效率会有所影响。

为了验证这些特点,请看下面的程序:

class program  
{  
  delegate void methodinvoker();  
  static void foo()  
  {  
    int intavailablethreads, intavailableioasynthreds;  
    threadpool.getavailablethreads(out intavailablethreads, out intavailableioasynthreds);  
    string strmessage = string.format(@"is thread pool: {0},  
    thread id: {1} free threads {2}",  
        thread.currentthread.isthreadpoolthread.tostring(),  
        thread.currentthread.gethashcode(),  
        intavailablethreads);  
    console.writeline(strmessage);  
    thread.sleep(10000);  
    return;  
  }  
 
  static void callfoo()  
  {  
    methodinvoker simpledelegate = new methodinvoker(foo);  
    for (int i = 0; i < 15; i++)  
    {  
      simpledelegate.begininvoke(null, null);  
    }  
  }  
  static void main(string[] args)  
  {  
    threadpool.setmaxthreads(10, 10);  
    callfoo();  
    console.readline();  
  }  
}

这个程序在起始的时候将线程池中最大线程个数设置为10个,然后做15次异步调用,每个异步调用中都停留10秒钟当作处理本身所要消耗的时间。从程序的执行我们可以看到,当前10个异步调用完全开始以后,新的异步调用就会等待(注意:不是主线程在等待),直到线程池中有线程空闲出来。

希望本文所述对大家的c#程序设计有所帮助。

上一篇:

下一篇: