分享

C#异步编程概述

 gingging 2012-11-02
C#异步编程概述

Windows 应用程序编程中常见的一个模式就是,在GUI用户界面下,将耗时的文件和网络处理放入子线程,以避免用户界面不能响应的问题。在.NET出现以前,创建线 程并监视线程结束,还要更新界面等工作,即复杂又要手写大量代码,并且难以调试。在.NET中,程序员可以通过异步调用,使用简单的代码完成这项工作。

.NET Framework允许异步调用任何方法。使用异步调用之前,要定义一个委托,它的方法签名要与调用方法签名一致。.NET会自动产生此委托的BeginInvoke和EndInvoke方法。

BeginInvoke 方法用于启动异步调用。它除了具有调用方法相同的参数外,还附加了两个额外的参数,这两个额外参数将用于回调方法。 BeginInvoke执行后立即返回,并不等待异步调用完成。 BeginInvoke返回一个IAsyncResult接口,它可以用于监视调用的进度。EndInvoke方法被用来获取异步调用的结果。在 BeginInvoke执行以后,任何时间都可以调用EndInvoke。如果异步调用尚未完成,EndInvoke会被阻塞,直到异步调用完成。EndInvoke带有一个IAsyncResult接口类型的参数,并返回一个IAsyncResult接口,用来获取调用结果。

本文中的代码演示了使用BeginInvoke和EndInvoke进行异步调用的四种常见调用方式。在调用BeginInvoke之后,您可以:

    1.完成一些其它工作,然后调用EndInvoke,等待异步调用完成。
    2.使用IAsyncResult.AsyncWaitHandle获取一个WaitHandle,使用它的WaitOne方法执行阻塞调用,完成后再调用EndInvoke。
    3.轮询BeginInvoke返回的IAsyncResult接口,在异步调用完成后,再调用EndInvoke。
    4.给BeginInvoke传递一个回调方法的委托,异步调用完成后,这个回调方法会在ThreadPool线程上执行,可以在这个方法内调用EndInvoke。

警告:异步调用完成后,必须调用EndInvoke。

测试方法和异步委托

在以下代码中,都使用同一个耗时的测试方法TestMethod。此方法向控制台输出信息,显示其开始执行,睡眠几秒钟,结束执行。TestMethod包含一个out参数,BeginInvoke和EndInvoke会同样包括它,你将来可以自己添加ref参数。


以下为TestMethod方法和它的委托。

    注意:为了便于说明,TestMethod被定义与与main()不同的一个类当中。当然,TestMethod也可以定义为一个与Main()方法同一个类当中的静态方法。


using System;
using System.Threading;

public class AsyncDemo {
    // 异步调用方法
    public string TestMethod(int callDuration, out int threadId) {
        Console.WriteLine("Test method begins.");
        Thread.Sleep(callDuration);
        threadId = AppDomain.GetCurrentThreadId();
        return "MyCallTime was " + callDuration.ToString();
    }
}

// 委托和调用方法有相同的签名
public delegate string AsyncDelegate(int callDuration, out int threadId);

众代表的字符串AsyncDelegate(整数callDuration,OUT INT的ThreadID);

调用EndInvoke,等待异步调用结束

异步调用最简单的方法是从BeginInvoke开始,在主线程中完成一些其它工作,然后调用EndInvoke,直到异步调用结束。对于文件和网络操作,这个技术不错。但是,由于主线程被阻塞于EndInvoke,该技术不适于有用户界面的应用程序。

public class AsyncMain {
    static void Main(string[] args) {
        // 异步调用使用的线程id
        int threadId;

        // 产生类实例
        AsyncDemo ad = new AsyncDemo();

        // 产生委托
        AsyncDelegate dlgt = new AsyncDelegate(ad.TestMethod);
   
        // 开始异步调用
        IAsyncResult ar = dlgt.BeginInvoke(3000,
            out threadId, null, null);

        Thread.Sleep(0);
        Console.WriteLine("Main thread {0} does some work.",
            AppDomain.GetCurrentThreadId());

        // 调用EndInvoke,阻塞并等待完成
        string ret = dlgt.EndInvoke(out threadId, ar);

        Console.WriteLine("The call executed on thread {0}, with return value \"{1}\".", threadId, ret);
    }
}

使用WaitHandle等待异步调用结束

等 待WaitHandle是一种常见的线程同步技术。BeginInvoke返回的IAsyncResult接口中,有一个AsyncWaitHandle 属性。通过调用它的WaitOne方法,等待异步调用结束。一旦异步调用结束,还可以有机会完成其它工作,最终调用EndInvoke并获得异步调用结 果。

public class AsyncMain {
    static void Main(string[] args) {
        // 异步调用使用的线程id
        int threadId;

        // 产生类实例
        AsyncDemo ad = new AsyncDemo();

        // 产生委托
        AsyncDelegate dlgt = new AsyncDelegate(ad.TestMethod);
   
        // 开始异步调用
        IAsyncResult ar = dlgt.BeginInvoke(3000,
            out threadId, null, null);

        Thread.Sleep(0);
        Console.WriteLine("Main thread {0} does some work.",
            AppDomain.GetCurrentThreadId());

        // 等待WaitHandle得到信号
        ar.AsyncWaitHandle.WaitOne();

        // 可以完成一些其它工作
        // 最后一定要调用EndInvoke
        string ret = dlgt.EndInvoke(out threadId, ar);

        Console.WriteLine("The call executed on thread {0}, with return value \"{1}\".", threadId, ret);
    }
}

轮询异步调用何时完成

可以使用由BeginInvoke返回的IAsyncResult接口的IsCompleted属性判断调用是否结束。用户界面线程可以轮询异步调用状态,同时又服务于用户输入。
 
public class AsyncMain {
    static void Main(string[] args) {
        // 异步调用使用的线程id
        int threadId;

        // 产生类实例
        AsyncDemo ad = new AsyncDemo();

        // 产生委托
        AsyncDelegate dlgt = new AsyncDelegate(ad.TestMethod);
   
        // 开始异步调用
        IAsyncResult ar = dlgt.BeginInvoke(3000,
            out threadId, null, null);
            
      // 轮询
        while(ar.IsCompleted == false) {
            Thread.Sleep(10);
        }

          // 最后一定要调用EndInvoke
        string ret = dlgt.EndInvoke(out threadId, ar);

        Console.WriteLine("The call executed on thread {0}, with return value \"{1}\".", threadId, ret);
    }
}

在异步调用完成时执行回调方法

如 果启动异步调用的线程不需要异步调用返回的结果,可以在异步调用结束时在ThreadPool线程上执行一个回调方法。通过AsyncCallback类 型委托,将这个回调方法传递BeginInvoke。这个回调方法可以带有一个对象,供它被调用时使用。例如,可以将启动异步调用的委托传递给回调方法, 在回调方法被调用时,执行EndInvoke。

public class AsyncMain {
    // Asynchronous method puts the thread id here.
    private static int threadId;

    static void Main(string[] args) {
        // 异步调用使用的线程id
        int threadId;

        // 产生类实例
        AsyncDemo ad = new AsyncDemo();

        // 产生委托
        AsyncDelegate dlgt = new AsyncDelegate(ad.TestMethod);
   
        // 启动异步调用,包含一个AsyncCallback类型的委托,即回调方法,和回调方法使用的参数,即启动异步调用的委托。
        IAsyncResult ar = dlgt.BeginInvoke(3000,
            out threadId,
            new AsyncCallback(CallbackMethod),
            dlgt );

        Console.WriteLine("Press Enter to close application.");
        Console.ReadLine();
    }
    
    // Callback method must have the same signature as the
    // AsyncCallback delegate.
    static void CallbackMethod(IAsyncResult ar) {
        // Retrieve the delegate.
        AsyncDelegate dlgt = (AsyncDelegate) ar.AsyncState;

        // Call EndInvoke to retrieve the results.
        string ret = dlgt.EndInvoke(out threadId, ar);

        Console.WriteLine("The call executed on thread {0}, with return value \"{1}\".", threadId, ret);
    }
}

此文源自MSDN:Asynchronous Programming Overview(http://msdn.microsoft.com/en-us/library/2e08f6yc%28v=vs.71%29.aspx)
译者:
孙钢

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多