异步调用

  1. 小明在烧水,等水烧开以后,将开水灌入热水瓶,然后开始整理家务
  2. 小文在烧水,在烧水的过程中整理家务,等水烧开以后,放下手中的家务活,将开水灌入热水瓶,然后继续整理家务

这也是日常生活中很常见的情形,小文的办事效率明显要高于小明。从C#程序执行的角度考虑,小明使用的同步处理方式,而小文则使用的异步处理方式。

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

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

1.class Program   
2.{   
3.    static TimeSpan Boil()   
4.    {   
5.        Console.WriteLine("水壶:开始烧水...");   
6.        Thread.Sleep(6000);   
7.        Console.WriteLine("水壶:水已经烧开了!");   
8.        return TimeSpan.MinValue;   
9.    }   
10.  
11.    delegate TimeSpan BoilingDelegate();   
12.  
13.    static void Main(string[] args)   
14.    {   
15.        Console.WriteLine("小文:将水壶放在炉子上");   
16.        BoilingDelegate d = new BoilingDelegate(Boil);   
17.        IAsyncResult result = d.BeginInvoke(BoilingFinishedCallback, null);   
18.        Console.WriteLine("小文:开始整理家务...");   
19.        for (int i = 0; i < 20; i++)   
20.        {   
21.            Console.WriteLine("小文:整理第{0}项家务...", i + 1);   
22.            Thread.Sleep(1000);   
23.        }   
24.    }   
25.  
26.    static void BoilingFinishedCallback(IAsyncResult result)   
27.    {   
28.        AsyncResult asyncResult = (AsyncResult)result;   
29.        BoilingDelegate del = (BoilingDelegate)asyncResult.AsyncDelegate;
30.        del.EndInvoke(result);
31.        Console.WriteLine("小文:将热水灌到热水瓶");   
32.        Console.WriteLine("小文:继续整理家务");   
33.           
34.    }   
35.}  

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

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

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

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

1.static TimeSpan Boil()   
2.{   
3.    DateTime begin = DateTime.Now;   
4.    Console.WriteLine("水壶:开始烧水...");   
5.    Thread.Sleep(6000);   
6.    Console.WriteLine("水壶:水已经烧开了!");   
7.    return DateTime.Now - begin;   
8.}   


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

1.static void BoilingFinishedCallback(IAsyncResult result)   
2.{   
3.    AsyncResult asyncResult = (AsyncResult)result;   
4.    BoilingDelegate del = (BoilingDelegate)asyncResult.AsyncDelegate;   
5.    Console.WriteLine("(烧水一共用去{0}时间)", del.EndInvoke(result));   
6.    Console.WriteLine("小文:将热水灌到热水瓶");   
7.    Console.WriteLine("小文:继续整理家务");   
8.}   


 

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

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

1.AsyncResult asyncResult = (AsyncResult)result;   
2.BoilingDelegate del = (BoilingDelegate)asyncResult.AsyncDelegate; 


 

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

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

  • 异步函数由线程完成,这个线程是.NET线程池中的线程
  • 通常情况下,.NET线程池拥有500个线程(当然这个数量可以设置),每当调用BeginInvoke开始异步处理时,异步处理函数就由线程池中的某个线程负责执行,而用户无法控制具体是由哪个线程负责执行
  • 由于线程池中线程数量有限,因此当池中线程被完全占用时,新的调用请求将使函数不得不等待空余线程的出现。此时,程序的效率会有所影响

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

1.class Program   
2.{   
3.    delegate void MethodInvoker();   
4.  
5.    static void Foo()   
6.    {   
7.        int intAvailableThreads, intAvailableIoAsynThreds;   
8.  
9.        ThreadPool.GetAvailableThreads(out intAvailableThreads,   
10.                out intAvailableIoAsynThreds);   
11.  
12.        string strMessage =   
13.            String.Format(@"Is Thread Pool: {0},   
14.        Thread Id: {1} Free Threads {2}",   
15.                Thread.CurrentThread.IsThreadPoolThread.ToString(),   
16.                Thread.CurrentThread.GetHashCode(),   
17.                intAvailableThreads);   
18.  
19.        Console.WriteLine(strMessage);   
20.  
21.        Thread.Sleep(10000);   
22.  
23.        return;   
24.    }   
25.  
26.    static void CallFoo()   
27.    {   
28.        MethodInvoker simpleDelegate =   
29.            new MethodInvoker(Foo);   
30.  
31.        for (int i = 0; i < 15; i++)   
32.        {   
33.            simpleDelegate.BeginInvoke(null, null);   
34.        }   
35.    }   
36.  
37.    static void Main(string[] args)   
38.    {   
39.        ThreadPool.SetMaxThreads(10, 10);   
40.        CallFoo();   
41.        Console.ReadLine();   
42.    }   
43.}   


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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在使用 Feign 进行异步调用时,你可以通过以下步骤来实现: 1. 配置 Feign 客户端:在你的 Spring Boot 项目中,添加 Feign 的依赖,并在配置类上加上 `@EnableFeignClients` 注解,以启用 Feign 客户端。 2. 创建 Feign 接口:定义一个接口,用于声明需要调用的远程服务的方法。你可以使用 `@FeignClient` 注解来指定所要调用的服务的名称,并使用 `@RequestMapping` 或其他相关注解来定义接口的请求路径和参数。 3. 实现异步调用:在你的 Feign 接口中,可以使用 `@Async` 注解将方法标记为异步调用。然后,你可以在方法体内使用 Spring 的异步支持(如 `CompletableFuture`)来处理异步任务。 以下是一个简单的示例: ```java @FeignClient(name = "remote-service") public interface RemoteServiceClient { @Async @GetMapping("/api/async") CompletableFuture<String> getAsyncData(); } ``` 在上面的示例中,`RemoteServiceClient` 接口定义了一个异步调用的方法 `getAsyncData()`,它将返回一个 `CompletableFuture` 对象,表示异步任务的结果。通过 `@FeignClient` 注解指定了需要调用的远程服务的名称。 然后,在你的业务代码中,你可以通过调用 `getAsyncData()` 方法来发起异步调用,并使用 `CompletableFuture` 来处理异步任务的结果。 需要注意的是,为了使 Feign 客户端支持异步调用,你还需要在项目的配置文件中增加以下配置: ```properties feign.hystrix.enabled=false ``` 这样,Feign 客户端就能够支持异步调用了。希望这能帮到你!如果还有其他问题,请随时提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值