异步编程模型--使用 IAsyncResult 对象

本文详细介绍了.NET Framework中异步编程的两种设计模式,并通过示例代码演示了如何使用IAsyncResult对象实现异步操作。此外,还探讨了不同场景下的异步调用处理方式,包括阻塞等待、轮询状态、通知机制等。

先推荐阅读下面的资料:

MSDN:异步编程设计模式

IBM developerworks: 使用异步 I/O 大大提高应用程序的性能

参考博文:

 

1、正确使用异步操作 

2、Lab:体会ASP.NET异步处理请求的效果

3、WCF中的异步调用

4、WCF从理论到实践(11)-异步

5、异步编程

 

.NET Framework 为异步操作提供两种设计模式:
1、使用 IAsyncResult 对象的异步操作。
2、使用事件的异步操作。

IAsyncResult接口类型

 

    [ComVisible( true )]
    
public   interface  IAsyncResult
    {
        
//  摘要:
        
//      获取用户定义的对象,它限定或包含关于异步操作的信息。
        
//
        
//  返回结果:
        
//      用户定义的对象,它限定或包含关于异步操作的信息。
         object  AsyncState {  get ; }
        
//
        
//  摘要:
        
//      获取用于等待异步操作完成的 System.Threading.WaitHandle。
        
//
        
//  返回结果:
        
//      用于等待异步操作完成的 System.Threading.WaitHandle。
        WaitHandle AsyncWaitHandle {  get ; }
        
//
        
//  摘要:
        
//      获取异步操作是否同步完成的指示。
        
//
        
//  返回结果:
        
//      如果异步操作同步完成,则为 true;否则为 false。
         bool  CompletedSynchronously {  get ; }
        
//
        
//  摘要:
        
//      获取异步操作是否已完成的指示。
        
//
        
//  返回结果:
        
//      如果操作完成则为 true,否则为 false。
         bool  IsCompleted {  get ; }
    }

 

下面是使用 IAsyncResult 对象的测试代码。

  public   delegate   string  AsyncDelegate( int  callDuration,  out   int  threadId);
    
class  Program
    {
        
static   void  Main( string [] args)
        {
            Fun1();
            Console.ReadLine();
        }

        
private   static   int  threadId;

        
// 阻塞等待   使用 EndInvoke 等待异步调用  
         static   void  Fun1()
        {
            
// 创建示例类的实例。
            AsyncDemo ad  =   new  AsyncDemo();
            
//  创建委托
            AsyncDelegate dlgt  =   new  AsyncDelegate(ad.TestMethod);
            
//  委托在这里开始异步调用。
            IAsyncResult ar  =  dlgt.BeginInvoke( 5000 , out  threadId,  null null );
            
// 人为的线程阻塞。
            Thread.Sleep( 0 );
            Console.WriteLine(
" 主线程 {0}开始工作 " ,Thread.CurrentThread.ManagedThreadId);
            
//  委托开始EndInvoke调用,这个过程会使主线程等待异步调用完成并返回结果。
             string  ret  =  dlgt.EndInvoke( out  threadId, ar);
            Console.WriteLine(
" 异步线程 {0},返回值 / " { 1 }/ " . " , threadId, ret);
            Console.WriteLine(
" 主线程{0}结束工作 " , Thread.CurrentThread.ManagedThreadId);
        }
        
        
// 阻塞等待  使用 WaitHandle 等待异步调用
         static   void  Fun2()
        {
            AsyncDemo ad 
=   new  AsyncDemo();
            AsyncDelegate dlgt 
=   new  AsyncDelegate(ad.TestMethod);
            IAsyncResult ar 
=  dlgt.BeginInvoke( 5000 , out  threadId,  null null );
            Thread.Sleep(
0 );
            Console.WriteLine(
" 主线程 {0}开始工作 " , Thread.CurrentThread.ManagedThreadId);
            
// 主线程在这里等待,直到异步线程执行完。
            ar.AsyncWaitHandle.WaitOne();
            
//  和前一方案的区别在于,你可以在异步调用完成后,获取异步调用返回值之前
            
// 在这里做点任何你想作的事。
            
// 调用EndInvoke获取异步调用的返回结果.
             string  ret  =  dlgt.EndInvoke( out  threadId, ar);
            Console.WriteLine(
" 异步线程 {0},返回值 / " { 1 }/ " . " , threadId, ret);
            Console.WriteLine(
" 主线程{0}结束工作 " , Thread.CurrentThread.ManagedThreadId);
        }

        
// 轮询状态    轮询异步调用完成
         static   void  Fun3()
        {
            AsyncDemo ad 
=   new  AsyncDemo();
            AsyncDelegate dlgt 
=   new  AsyncDelegate(ad.TestMethod);
            IAsyncResult ar 
=  dlgt.BeginInvoke( 5000 , out  threadId,  null null );
            Console.WriteLine(
" 主线程 {0}开始工作 " , Thread.CurrentThread.ManagedThreadId);
            
// 这里每隔10毫秒就检测(轮询)一下异步执行的状态,
            
// 直到异步调用完成,IsCompleted的值变为ture为止。
             while  (ar.IsCompleted  ==   false )
            {
                Thread.Sleep(
10 );
            }

            
// 还记得微软的那个善意的提醒吗?虽然IsCompleted为true了,
            
// 我们还是调用一下EndInvoke,来获取返回值。
             string  ret  =  dlgt.EndInvoke( out  threadId, ar);
            Console.WriteLine(
" 异步线程 {0},返回值 / " { 1 }/ " . " , threadId, ret);
            Console.WriteLine(
" 主线程{0}结束工作 " , Thread.CurrentThread.ManagedThreadId);
        }

        
// 通知机制    异步调用完成时执行回调方法
         static   void  Fun4()
        {
            AsyncDemo ad 
=   new  AsyncDemo();
            AsyncDelegate dlgt 
=   new  AsyncDelegate(ad.TestMethod);
            
// 注意第三个参数,这就是我们要用到的回调方法。
            
// 第四个参数更为有趣,它可以是任何Object对象,这里它就是
            
// 执行异步调用的委托本身,把委托本身传递进去的原因在下面可以看到。
            Console.WriteLine( " 主线程 {0}开始工作 " , Thread.CurrentThread.ManagedThreadId);
            IAsyncResult ar 
=  dlgt.BeginInvoke( 5000 , out  threadId,  new  AsyncCallback(CallbackMethod), dlgt);
            Console.WriteLine(
" 主线程 {0}结束工作 " , Thread.CurrentThread.ManagedThreadId);
            Console.ReadLine();
        }
        
// 回调函数必须严格的遵照AsyncCallback委托的签名。
         static   void  CallbackMethod(IAsyncResult ar)
        {
            
// 在这里,上面那个dlgt作为参数的作用得到了体现,原来它就是为了完成对EndInvoke的调用啊。
            AsyncDelegate dlgt  =  (AsyncDelegate)ar.AsyncState;
            
// 通过对EndInvoke的调用获取返回值。
             string  ret  =  dlgt.EndInvoke( out  threadId, ar);
            Console.WriteLine(
" 异步线程 {0},返回值 / " { 1 }/ " . " , threadId, ret);
        }
    }
    
// 使用异步编程模型 
     public   class  AsyncDemo
    {
        
public   string  TestMethod( int  callDuration,  out   int  threadId)
        {
            Console.WriteLine(
" 异步方法开始工作 " );
            Thread.Sleep(callDuration);
            threadId 
=  Thread.CurrentThread.ManagedThreadId; 
            
return   " 异步方法执行时间  "   +  callDuration.ToString();
        }
    }

 执行结果:

Fun1--------------------

异步方法开始工作
主线程 10开始工作
异步线程 7,返回值 "异步方法执行时间 5000".
主线程10结束工作
Fun2---------------------
异步方法开始工作
主线程 10开始工作
异步线程 7,返回值 "异步方法执行时间 5000".
主线程10结束工作
Fun3---------------------

主线程 10开始工作
异步方法开始工作
异步线程 7,返回值 "异步方法执行时间 5000".
主线程10结束工作
Fun4---------------------
主线程 10开始工作
主线程 10结束工作
异步方法开始工作
异步线程 7,返回值 "异步方法执行时间 5000".
--------------------------

工作当中有一个调用ActiveMQ的产品,每天大概是上万的消息。压力也不是很大。最近总是出现调用MQ阻塞的情况。负责MQ的同事也没有找到原因。

我是想寻找一个消息队列的代替品。 上面的第四个方法,异步线程不阻塞调用线程,当然调用线程也无需处理调用结果,貌似可以作为一个代替方案。

 

在.NET框架中,异步编程模型(APM)是一种广泛使用编程模式,旨在提高应用程序的性能和响应能力。它允许开发人员在执行耗时操作(如网络请求、文件读写、复杂计算等)时,避免阻塞主线程,从而提升用户体验和资源利用率。以下将从基本概念、实现方式以及最佳实践三个方面,深入探讨.NET框架中异步编程模型使用方法。 ### 异步编程的基本概念 异步编程的核心是通过异步方法调用实现非阻塞操作。在.NET中,`async`和`await`关键字是实现异步编程的核心语法支持。通过`async`标记一个方法为异步方法,而`await`用于等待异步操作的完成,而不会阻塞当前线程。例如: ```csharp public async Task<string> DownloadDataAsync() { using (HttpClient client = new HttpClient()) { return await client.GetStringAsync("https://api.example.com/data"); } } ``` 此代码示例展示了如何使用`HttpClient`异步获取数据[^3]。该方法返回一个`Task<string>`对象,表示一个尚未完成的操作,调用方可以使用`await`等待其完成。 ### 异步编程的实现方式 在.NET框架中,异步编程模型可以通过多种方式实现: 1. **基于任务的异步模式(TAP)**:这是现代.NET开发中最常用的方式。它使用`Task`和`Task<T>`类型表示异步操作的结果。例如,`HttpClient.GetStringAsync()`、`Entity Framework`的`SaveChangesAsync()`等都是TAP的典型应用[^3]。 2. **基于事件的异步模式(EAP)**:这是一种较早的异步编程模式,通常通过事件和回调函数来处理异步操作的完成。例如,`WebClient`类中的`DownloadStringCompleted`事件。 3. **异步编程模型(APM)**:通过`IAsyncResult`接口和`BeginXXX`/`EndXXX`方法实现异步操作。例如,`FileStream.BeginRead`和`FileStream.EndRead`方法对文件进行异步读取操作。这种方式较为复杂,但在某些特定场景下仍然有用[^4]。 ### 异步编程的最佳实践 在使用异步编程模型时,有一些关键的最佳实践需要注意: - **避免阻塞异步代码**:不要使用`Result`或`Wait()`方法强制等待异步操作完成,这可能导致死锁,尤其是在UI或ASP.NET上下文中。 - **正确处理异常**:异步代码中的异常应使用`try/catch`块捕获,并通过`await`捕获异常。例如: ```csharp try { var data = await DownloadDataAsync(); Process(data); } catch (HttpRequestException ex) { LogError(ex); } ``` 该示例展示了如何在异步代码中捕获异常。 - **合理使用`ConfigureAwait(false)`**:在库代码中,建议使用`ConfigureAwait(false)`避免上下文捕获,以提高性能并减少死锁风险。 - **区分I/O密集型与CPU密集型操作**:对于I/O密集型操作(如网络请求、数据库查询),应使用异步方法;而对于CPU密集型任务(如图像处理、加密计算),应使用`Task.Run`将任务卸载到线程池中,以避免阻塞主线程[^3]。 ### 异步编程在现代.NET框架中的改进 在.NET 8中,异步编程模型得到了进一步的优化,特别是在性能和开发体验方面。例如,新增的`ValueTask`和`IAsyncDisposable`接口,使得异步资源管理更加高效和灵活。这些改进使得开发者能够更高效地编写异步代码,从而提升应用程序的性能和响应能力[^2]。 此外,.NET生态系统的大多数现代库(如`HttpClient`、`Entity Framework`、`Redis`客户端、`Azure SDK`等)都提供了异步方法,与异步编程无缝集成。这使得异步编程成为现代.NET开发的标准实践[^3]。 ### 异步编程的适用场景 异步编程模型适用于多种场景,包括但不限于: - **Web请求**:如使用`HttpClient`发送HTTP请求。 - **数据库操作**:如使用`Entity Framework`进行异步查询和更新。 - **文件读写**:如使用`FileStream`进行异步读写。 - **并行计算**:如使用`Parallel`类或`Task.Run`进行多线程处理。 通过合理使用异步编程模型,可以显著提升应用程序的并发处理能力和响应速度,尤其是在高并发或I/O密集型的场景中。 ---
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值