异步调用同步方法

Calling Synchronous Methods Asynchronously

异步调用同步方法


一 介绍

The .NET Framework enables you to call any method asynchronously. To do this you define a delegate with the same signature as the method you want to call; the common language runtime automatically defines BeginInvoke and EndInvoke methods for this delegate, with the appropriate signatures.
.NET Framework 允许你异步调用任何方法。为此你需要定义一个与该方法相同签名的委托;公共语言运行时自动以适当的名称为该委托定义BeginInvoke和EndInvoke方法。

!注意
Asynchronous delegate calls, specifically the BeginInvoke and EndInvoke methods, are not supported in the .NET Compact Framework.
异步委托的调用,确切的说是BeginInvoke和EndInvoke方法,在.NET Compact Framework中不支持。

The BeginInvoke method initiates the asynchronous call. It has the same parameters as the method that you want to execute asynchronously, plus two additional optional parameters. The first parameter is an AsyncCallback delegate that references a method to be called when the asynchronous call completes. The second parameter is a user-defined object that passes information into the callback method. BeginInvoke returns immediately and does not wait for the asynchronous call to complete. BeginInvoke returns an IAsyncResult, which can be used to monitor the progress of the asynchronous call.
BeginInvoke方法发起异步调用。它与你要异步执行的方法有相同的参数,并多了两个可选参数。第一个可选参数是一个异步回调委托,指向一个异步调用完成时调用的方法。第二个参数是一个用户自定义实例,传给回调方法信息。BeginInvoke立即返回,不等异步调用完成。其返回一个IAsyncResult接口,用来监视异步调用的进程。

The EndInvoke method retrieves the results of the asynchronous call. It can be called any time after BeginInvoke. If the asynchronous call has not completed, EndInvoke blocks the calling thread until it completes. The parameters of EndInvoke include the out and ref parameters ( ByRef and ByRef in Visual Basic) of the method that you want to execute asynchronously, plus the IAsyncResult returned by BeginInvoke.
EndInvoke方法取回异步调用的结果。其可以在BeginInvoke之后任何时候调用。如果异步调用没有完成,EndInvoke方法会阻塞调用进程,直到调用结束。EndInvoke的参数包括一个out和一个ref参数,如果你想异步执行,还有BeginInvoke返回的IAsyncResult接口。

!注意
The IntelliSense feature in Visual Studio displays the parameters of BeginInvoke and EndInvoke. If you’re not using Visual Studio or a similar tool, or if you’re using C# with Visual Studio, see Asynchronous Programming Model (APM) for a description of the parameters defined for these methods.
VS里的智能感知功能展示了BeginInvoke和EndInvoke的参数。如果你不在用VS或其他类似工具,或者你用着VS的C#,看看异步程序范例里这些参数的描述。

The code examples in this topic demonstrate four common ways to use BeginInvoke and EndInvoke to make asynchronous calls. After calling BeginInvoke you can do the following:
代码例子演示了四种通常如何使用BeginInvoke和EndInvoke做异步调用。在调用BegeinInvoke后你可以如下操作:

Do some work and then call EndInvoke to block until the call completes.
1 进行一些操作,然后调用EndInvoke阻塞进程直到调用完成。

Obtain a WaitHandle using the IAsyncResult.AsyncWaitHandle property, use its WaitOne method to block execution until the WaitHandle is signaled, and then call EndInvoke.
2 获取一个有IAsyncResult.AsyncWaitHandle属性的等待句柄,使用它的WaitOne方法阻塞执行直到等待句柄发出信号,然后调用EndInvoke。

Poll the IAsyncResult returned by BeginInvoke to determine when the asynchronous call has completed, and then call EndInvoke.
3 检查BeginInvoke返回的IAsyncResult以确定异步调用何时完成,然后调用EndInvoke

Pass a delegate for a callback method to BeginInvoke. The method is executed on a ThreadPool thread when the asynchronous call completes. The callback method calls EndInvoke.
4 向BeginInvoke传递一个回调方法的委托。当异步调用完成时,该方法被线程池的线程执行。该方法调用EndInvoke。

!重要
No matter which technique you use, always call EndInvoke to complete your asynchronous call.

无论你用何种技术,总要调用EndInvoke方法完成异步调用。

二 实例

1 Defining the Test Method and Asynchronous Delegate

1 定义测试方法和异步委托

The code examples that follow demonstrate various ways of calling the same long-running method, TestMethod, asynchronously. The TestMethod method displays a console message to show that it has begun processing, sleeps for a few seconds, and then ends. TestMethod has an out parameter to demonstrate the way such parameters are added to the signatures of BeginInvoke and EndInvoke. You can handle ref parameters similarly.
代码示例演示了多种方式异步调用同样长程方法——TestMethod。TestMethod方法展示了控制信息表达程序开始,睡眠若干秒,然后结束。TestMethod有一个输出参数用以演示参数被加入BeginInvoke和EndInvoke声明。你可以用ref参数做类似处理。

The following code example shows the definition of TestMethod and the delegate named AsyncMethodCaller that can be used to call TestMethod asynchronously. To compile the code examples, you must include the definitions for TestMethod and the AsyncMethodCaller delegate.
下面的代码例子展示了TestMethod方法和名为“AsyncMethodCaller”委托的的定义,该委托可用来异步调用TestMethod. 要编译这段代码,你得包含TestMethod和AsyncMethodCaller委托的定义。

using System;
using System.Threading; 

namespace AsyncTest
{
    public class AsyncDemo
    {
        //the method execute asynchronously
        public string TestMethod(int callDuration,out int threadId)
        {
            Console.WriteLine("TestMethod begins");
            Thread.Sleep(callDuration);
            threadId = Thread.CurrentThread.ManagedThreadId;
            for(int i = 0; i < 10; i++)
            {
                Console.WriteLine("测试方法执行" + i);
            }
            return string.Format("TestMethod call time is {0}",callDuration);
        }
    }

    //the delegate have the same signature with the method, will call asynchronously
    public delegate string AsyncMethodCaller(int callDuration, out int threadId);
}

2 Waiting for an Asynchronous Call with EndInvoke

2 用EndInvoke阻塞主线程等待异步调用结束

The simplest way to execute a method asynchronously is to start executing the method by calling the delegate’s BeginInvoke method, do some work on the main thread, and then call the delegate’s EndInvoke method. EndInvoke might block the calling thread because it does not return until the asynchronous call completes. This is a good technique to use with file or network operations.
最简单的方式异步执行一个方法是:调用委托的BeginInvoke方法执行该方法,在主线程进行其他操作,然后调用委托的EndInvoke方法。EndInvoke方法可能会阻塞该异步线程,因为它直到主线程操作结束后才返回。这是用在文件操作和网络操作上的好技术。
(作者:如果不调用EndInvoke方法,程序会立即退出,这是由于使用BeginInvoke创建的线程都是后台线程,这种线程一但所有的前台线程都退出后(其中主线程就是一个前台线程),不管后台线程是否执行完毕,都会结束线程,并退出程序。)

!注意
Because EndInvoke might block, you should never call it from threads that service the user interface.
因为EndInvoke会阻塞调用线程,你不应在用户界面的线程中调用它。

(作者:会造成用户界面假死)

using System;
using System.Threading;

namespace AsyncTest
{
    public class AsyncMain
    {
        public static void Main()
        {
            //the asynchronous method puts the thread id here
            int threadId;

            //create an instance of test class
            AsyncDemo ad = new AsyncDemo();

            //create the deletgate
            AsyncMethodCaller caller = new AsyncMethodCaller(ad.TestMethod);

            //initiate the asychronous call
            IAsyncResult result = caller.BeginInvoke(3000, out threadId, null, null);



            for (int i = 0;i < 20; i++)
            {
                Console.WriteLine("Main thread {0} does some work{1}.", Thread.CurrentThread.ManagedThreadId,i);
            }


            string returnValue = caller.EndInvoke(out threadId, result);

            // 说明主线程受到EndInvoke阻塞
            Console.WriteLine("The call use thread {0}, the return value : \" {1} \"", threadId, returnValue);
            Console.ReadKey();

        }
    }
}

/* This example produces output similar to the following:

Main thread 1 does some work.
Test method begins.
The call executed on thread 3, with return value "My call time was 3000.".
 */

3 Waiting for an Asynchronous Call with WaitHandle

3 用WaitHandle等待异步调用

You can obtain a WaitHandle by using the AsyncWaitHandle property of the IAsyncResult returned by BeginInvoke. The WaitHandle is signaled when the asynchronous call completes, and you can wait for it by calling the WaitOne method.
你可以用 BeginInvoke返回的IAsyncResult接口的AsyncWaitHandle属性 获取一个WaitHandle句柄。当异步调用完成时WaitHandle被发出,你可以调用WaitOne 方法等待接受这个信息。

If you use a WaitHandle, you can perform additional processing before or after the asynchronous call completes, but before calling EndInvoke to retrieve the results.
如果你用了WaitHandle,你可以在异步调用结束前后进行其他操作,但之后必须用EndInvoke取回结果。

! Important
!注意
The wait handle is not closed automatically when you call EndInvoke. If you release all references to the wait handle, system resources are freed when garbage collection reclaims the wait handle. To free the system resources as soon as you are finished using the wait handle, dispose of it by calling the WaitHandle.Close method. Garbage collection works more efficiently when disposable objects are explicitly disposed.
WaitHandle不会在你调用EndInvoke后自动关闭。如果你释放所有等待句柄的引用,当垃圾收集器回收等待句柄时,系统资源会被重新释放。为了在用完等待句柄后尽快释放系统资源,尽量使用WaitHandle的Close方法。可丢弃的对象被明确标记为丢弃,垃圾收集器会更有效的工作。

using System;
using System.Threading;

namespace Examples.AdvancedProgramming.AsynchronousOperations
{
    public class AsyncMain 
    {
        static void Main() 
        {
            // The asynchronous method puts the thread id here.
            int threadId;

            // Create an instance of the test class.
            AsyncDemo ad = new AsyncDemo();

            // Create the delegate.
            AsyncMethodCaller caller = new AsyncMethodCaller(ad.TestMethod);
       
            // Initiate the asychronous call.
            IAsyncResult result = caller.BeginInvoke(3000, 
                out threadId, null, null);

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

            // Wait for the WaitHandle to become signaled.
            //等待WaitHandle收到信号
            result.AsyncWaitHandle.WaitOne();
            //作者:WaitOne的第一个参数表示要等待的毫秒数,在指定时间之内,WaitOne方法将一直等待,直到异步调用完成,并发出通知,WaitOne方法才返回true。当等待指定时间之后,异步调用仍未完成,WaitOne方法返回false,如果指定时间为0,表示不等待,如果为-1,表示永远等待,直到异步调用完成。

            // Perform additional processing here.
            // Call EndInvoke to retrieve the results.
            string returnValue = caller.EndInvoke(out threadId, result);

            // Close the wait handle.
            result.AsyncWaitHandle.Close();

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

/* This example produces output similar to the following:

Main thread 1 does some work.
Test method begins.
The call executed on thread 3, with return value "My call time was 3000.".
 */

4 Polling for Asynchronous Call Completion

4 检查异步调用是否完成

You can use the IsCompleted property of the IAsyncResult returned by BeginInvoke to discover when the asynchronous call completes. You might do this when making the asynchronous call from a thread that services the user interface. Polling for completion allows the calling thread to continue executing while the asynchronous call executes on a ThreadPool thread.
你可以用 BeginInvoke返回的 IAyncResult对象的 IsCompleted 属性 查看异步调用是否完成。 你或许会在为用户界面服务的异步调用进程中这么做。检查完成的活动允许调用线程在线程池的异步线程执行的时候自己也在执行。

using System;
using System.Threading;

namespace Examples.AdvancedProgramming.AsynchronousOperations
{
    public class AsyncMain 
    {
        static void Main() {
            // The asynchronous method puts the thread id here.
            int threadId;

            // Create an instance of the test class.
            AsyncDemo ad = new AsyncDemo();

            // Create the delegate.
            AsyncMethodCaller caller = new AsyncMethodCaller(ad.TestMethod);
       
            // Initiate the asychronous call.
            IAsyncResult result = caller.BeginInvoke(3000, 
                out threadId, null, null);

            // Poll while simulating work.
            while(result.IsCompleted == false) {
                Thread.Sleep(250);
                Console.Write(".");
            }

            // Call EndInvoke to retrieve the results.
            //调用EndInvoke取回结果
            //(作者:这里再使用EndInvoke,由于result.IsCompleted=true,调用已经结束,就不会再阻塞主线程)
            string returnValue = caller.EndInvoke(out threadId, result);

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

/* This example produces output similar to the following:

Test method begins.
.............
The call executed on thread 3, with return value "My call time was 3000.".
 */

5 Executing a Callback Method When an Asynchronous Call Completes

5 当异步调用完成时执行一个回调函数

If the thread that initiates the asynchronous call does not need to be the thread that processes the results, you can execute a callback method when the call completes. The callback method is executed on a ThreadPool thread.
如果发起异步调用的线程不必要是得出结果的线程,你可以在调用完成后执行一个回调函数。这个回调函数会由线程池里的线程执行。

To use a callback method, you must pass BeginInvoke an AsyncCallback delegate that represents the callback method. You can also pass an object that contains information to be used by the callback method. In the callback method, you can cast the IAsyncResult, which is the only parameter of the callback method, to an AsyncResult object. You can then use the AsyncResult.AsyncDelegate property to get the delegate that was used to initiate the call so that you can call EndInvoke.
为了使用回调函数,你得给BeginInvoke传递一个代表回调函数的AsyncCallback委托作为参数。还可以传递一个包含回调函数用到的信息的对象作为参数。在回调函数中,你可以实现IAsyncResult接口——回调函数唯一一个参数——使之成为一个AsyncResult对象。然后你可以使用AsyncResult的AsyncDelegate属性获取用来发起调用的委托,以便于你调用EndInvoke。

Notes on the example:
例子里需要注意的地方

The threadId parameter of TestMethod is an out parameter ([ ByRef in Visual Basic), so its input value is never used by TestMethod. A dummy variable is passed to the BeginInvoke call. If the threadId parameter were a ref parameter (ByRef in Visual Basic), the variable would have to be a class-level field so that it could be passed to both BeginInvoke and EndInvoke.
TestMethod方法中threadId参数是一个输出参数,所以它的输入值不会被TestMethod所用。一个虚拟变量传给BeginInvoke调用。如果threadId参数是一个指针参数,该变量将成为一个类级别字段,因此它可以被传递给BeginInvoke和EndInvoke。

The state information that is passed to BeginInvoke is a format string, which the callback method uses to format an output message. Because it is passed as type Object, the state information must be cast to its proper type before it can be used.
被传给BeginInvoke的状态信息是一个格式化字符串,给回调函数用来格式化输出信息。因为如同对象类型一样被传入,所以状态信息必须在使用前被改造成恰当的类型。

The callback is made on a ThreadPool thread. ThreadPool threads are background threads, which do not keep the application running if the main thread ends, so the main thread of the example has to sleep long enough for the callback to finish.
线程池的线程做回调,而线程池线程都是后台线程,在程序主线程结束后不需要保持程序运行,因此例子中的主线程休眠了足够长时间直到回调完成。

using System;
using System.Threading;
using System.Runtime.Remoting.Messaging;

namespace Examples.AdvancedProgramming.AsynchronousOperations
{
    public class AsyncMain 
    {
        static void Main() 
        {
            // Create an instance of the test class.
            AsyncDemo ad = new AsyncDemo();

            // Create the delegate.
            AsyncMethodCaller caller = new AsyncMethodCaller(ad.TestMethod);
       
            // The threadId parameter of TestMethod is an out parameter, so
            // its input value is never used by TestMethod. Therefore, a dummy
            // variable can be passed to the BeginInvoke call. If the threadId
            // parameter were a ref parameter, it would have to be a class-
            // level field so that it could be passed to both BeginInvoke and 
            // EndInvoke.
            int dummy = 0;

            // Initiate the asynchronous call, passing three seconds (3000 ms)
            // for the callDuration parameter of TestMethod; a dummy variable 
            // for the out parameter (threadId); the callback delegate; and
            // state information that can be retrieved by the callback method.
            // In this case, the state information is a string that can be used
            // to format a console message.
            IAsyncResult result = caller.BeginInvoke(3000,
                out dummy, 
                new AsyncCallback(CallbackMethod),
                "The call executed on thread {0}, with return value \"{1}\".");

            Console.WriteLine("The main thread {0} continues to execute...", 
                Thread.CurrentThread.ManagedThreadId);

            // The callback is made on a ThreadPool thread. ThreadPool threads
            // are background threads, which do not keep the application running
            // if the main thread ends. Comment out the next line to demonstrate
            // this.
            Thread.Sleep(4000);

            Console.WriteLine("The main thread ends.");
        }
        
        // The callback method must have the same signature as the
        // AsyncCallback delegate.
        static void CallbackMethod(IAsyncResult ar) 
        {
            // Retrieve the delegate.
            AsyncResult result = (AsyncResult) ar;
            AsyncMethodCaller caller = (AsyncMethodCaller) result.AsyncDelegate;

            // Retrieve the format string that was passed as state 
            // information.
            string formatString = (string) ar.AsyncState;

            // Define a variable to receive the value of the out parameter.
            // If the parameter were ref rather than out then it would have to
            // be a class-level field so it could also be passed to BeginInvoke.
            int threadId = 0;

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

            // Use the format string to format the output message.
            Console.WriteLine(formatString, threadId, returnValue);
        }
    }
}

/* This example produces output similar to the following:

The main thread 1 continues to execute...
Test method begins.
The call executed on thread 3, with return value "My call time was 3000.".
The main thread ends.
 */

(作者:在GUI程序中,使用上面三种用到EndInvoke的方法,都会是程序界面假死,要想在异步调用时,程序界面其他部分仍能正常工作,就要用回调函数的方式。)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值