Calling Synchronous Methods Asynchronously
콘텐츠의 테이블 축소
콘텐츠의 테이블 확장
문서를 영문으로 보려면 영문 확인란을 선택하세요. 마우스 포인터를 텍스트 위로 이동시켜 팝업 창에서 영문 텍스트를 표시할 수도 있습니다.
번역
영문

Calling Synchronous Methods Asynchronously

 

.NET Framework에서는 모든 메서드를 비동기 방식으로 호출할 수 있습니다. 이렇게 하려면 호출하려는 메서드와 같은 시그니처를 사용하여 대리자를 정의합니다. 그러면 공용 언어 런타임은 이 대리자에 대해 BeginInvokeEndInvoke 메서드를 해당 시그니처와 함께 자동으로 정의합니다.

System_CAPS_note참고

특히 BeginInvokeEndInvoke 메서드와 같은 비동기 대리자는 .NET Compact Framework에서 호출할 수 없습니다.

BeginInvoke 메서드는 비동기 호출을 시작합니다. 이 메서드의 매개 변수는 비동기 방식으로 실행하려는 메서드의 매개 변수와 같으며 두 개의 선택적인 매개 변수가 추가로 사용됩니다. 첫 번째 매개 변수는 비동기 호출이 완료될 때 호출될 메서드를 참조하는 AsyncCallback 대리자이고 두 번째 매개 변수는 콜백 메서드에 정보를 전달하는 사용자 정의 개체입니다. BeginInvoke는 비동기 호출이 완료되기를 기다리지 않고 즉시 반환합니다. BeginInvoke는 비동기 호출의 진행률을 모니터링하는 데 사용할 수 있는 IAsyncResult를 반환합니다.

EndInvoke 메서드는 비동기 호출의 결과를 검색합니다. 이 메서드는 BeginInvoke를 호출한 후 언제든지 호출할 수 있습니다. 비동기 호출이 완료되지 않은 경우 EndInvoke는 호출이 완료될 때까지 호출하는 스레드를 차단합니다. EndInvoke의 매개 변수에는 비동기 방식으로 실행하려는 메서드의 outref 매개 변수(Visual Basic의 경우 <Out> ByRefByRef)와 IAsyncResult에서 반환하는 BeginInvoke가 포함됩니다.

System_CAPS_note참고

Visual Studio 2005의 IntelliSense 기능에서는 BeginInvokeEndInvoke의 매개 변수를 표시합니다. Visual Studio 또는 이와 유사한 도구를 사용하지 않거나 Visual Studio 2005와 C#을 함께 사용하는 경우 이러한 메서드에 대해 정의된 매개 변수에 대한 설명을 보려면 Asynchronous Programming Model (APM)을 참조하세요.

이 항목의 코드 예제에서는 BeginInvokeEndInvoke를 사용하여 비동기 호출을 수행하는 네 가지 일반적인 방법을 보여 줍니다. BeginInvoke를 호출한 후 다음과 같은 작업을 수행할 수 있습니다.

  • 작업을 수행한 다음 EndInvoke를 호출하여 호출이 완료될 때까지 실행을 차단합니다.

  • IAsyncResult.AsyncWaitHandle 속성을 사용하여 WaitHandle을 가져오고 해당 WaitOne 메서드를 사용하여 WaitHandle이 신호를 받을 때까지 실행을 차단한 다음 EndInvoke를 호출합니다.

  • BeginInvoke에서 반환한 IAsyncResult를 폴링하여 비동기 호출이 완료되는 시점을 확인한 다음 EndInvoke를 호출합니다.

  • 콜백 메서드의 대리자를 BeginInvoke에 전달합니다. 이 메서드는 비동기 호출이 완료될 때 ThreadPool 스레드에서 실행됩니다. 콜백 메서드는 EndInvoke를 호출합니다.

System_CAPS_important중요

사용하는 방법에 관계없이 항상 EndInvoke를 호출하여 비동기 호출을 완료해야 합니다.

다음 코드 예제에서는 동일한 장기 실행 메서드인 TestMethod를 비동기 방식으로 호출하는 다양한 방법을 보여 줍니다. TestMethod 메서드는 처리를 시작했음을 나타내는 콘솔 메시지를 표시하고 몇 초간 대기한 후 종료됩니다. TestMethod에는 BeginInvokeEndInvoke의 시그니처에 해당 매개 변수가 추가되는 방식을 보여 주는 out 매개 변수가 있습니다. ref 매개 변수도 마찬가지로 처리할 수 있습니다.

다음 코드 예제에서는 TestMethod의 정의와 TestMethod를 비동기식으로 호출하는 데 사용할 수 있는 AsyncMethodCaller라는 대리자를 보여 줍니다. 코드 예제를 컴파일하려면 TestMethodAsyncMethodCaller 대리자의 정의를 포함해야 합니다.

using System;
using System.Threading; 

namespace Examples.AdvancedProgramming.AsynchronousOperations
{
    public class AsyncDemo 
    {
        // The method to be executed asynchronously.
        public string TestMethod(int callDuration, out int threadId) 
        {
            Console.WriteLine("Test method begins.");
            Thread.Sleep(callDuration);
            threadId = Thread.CurrentThread.ManagedThreadId;
            return String.Format("My call time was {0}.", callDuration.ToString());
        }
    }
    // The delegate must have the same signature as the method
    // it will call asynchronously.
    public delegate string AsyncMethodCaller(int callDuration, out int threadId);
}

메서드를 비동기 방식으로 실행하는 가장 간단한 방법은 대리자의 BeginInvoke 메서드를 호출하여 메서드 실행을 시작하고 주 스레드에서 작업을 수행한 다음 대리자의 EndInvoke 메서드를 호출하는 것입니다. EndInvoke는 비동기 호출이 완료될 때까지 반환되지 않으므로 호출하는 스레드를 차단할 수도 있습니다. 이 방법은 파일 또는 네트워크 작업에 적합합니다.

System_CAPS_important중요

EndInvoke는 실행을 차단할 수 있으므로 사용자 인터페이스를 제공하는 스레드에서는 호출하지 말아야 합니다.

using System;
using System.Threading;

namespace Examples.AdvancedProgramming.AsynchronousOperations
{
    public class AsyncMain 
    {
        public 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);

            // Call EndInvoke to wait for the asynchronous call to complete,
            // and to retrieve the results.
            string returnValue = caller.EndInvoke(out threadId, result);

            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.".
 */

BeginInvoke에서 반환하는 IAsyncResultAsyncWaitHandle 속성을 사용하여 WaitHandle을 가져올 수 있습니다. 비동기 호출이 완료되면 WaitHandle은 신호를 받으며 WaitOne 메서드를 호출하여 대기할 수 있습니다.

WaitHandle을 사용하는 경우 비동기 호출이 완료되기 전이나 후에 추가 작업을 처리한 다음 EndInvoke를 호출하여 결과를 검색할 수 있습니다.

System_CAPS_note참고

대기 핸들은 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.
            result.AsyncWaitHandle.WaitOne();

            // 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.".
 */

BeginInvoke에서 반환하는 IAsyncResultIsCompleted 속성을 사용하여 비동기 호출이 완료되는 시점을 확인할 수 있습니다. 사용자 인터페이스를 제공하는 스레드에서 비동기 호출을 수행하는 경우 이 동작을 수행할 수 있습니다. 완료에 대해 폴링하면 비동기 호출이 ThreadPool 스레드에서 실행되는 동안 호출 스레드가 계속 실행될 수 있습니다.

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.
            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.".
 */

비동기 호출을 시작하는 스레드와 결과를 처리하는 스레드가 서로 다를 수 있는 경우에는 호출이 완료될 때 콜백 메서드를 실행할 수 있습니다. 콜백 메서드는 ThreadPool 스레드에서 실행됩니다.

콜백 메서드를 사용하려면 BeginInvoke에 콜백 메서드를 나타내는 AsyncCallback 대리자를 전달해야 합니다. 콜백 메서드에서 사용할 정보가 들어 있는 개체를 전달할 수도 있습니다. 콜백 메서드에서는 콜백 메서드의 유일한 매개 변수인 IAsyncResultAsyncResult 개체로 캐스팅할 수 있습니다. 그런 다음 AsyncResult.AsyncDelegate 속성을 사용하여 호출을 시작하는 데 사용된 대리자를 가져오면 EndInvoke를 호출할 수 있습니다.

예제 관련 참고 사항:

  • threadIdTestMethod 매개 변수는 out 매개 변수(Visual Basic의 경우 <Out> ByRef)이므로 입력 값은 TestMethod에서 사용되지 않습니다. BeginInvoke를 호출할 때는 더미 변수가 전달됩니다. threadId 매개 변수가 ref 매개 변수(Visual Basic의 경우 ByRef)인 경우에는 변수가 BeginInvokeEndInvoke에 전달 가능한 클래스 수준 필드여야 합니다.

  • BeginInvoke에 전달되는 상태 정보는 콜백 메서드에서 출력 메시지의 형식을 지정하는 데 사용하는 형식 문자열입니다. 이 문자열은 Object 형식으로 전달되므로 상태 정보를 적절한 형식으로 캐스팅해야 사용할 수 있습니다.

  • 콜백은 ThreadPool 스레드에서 수행됩니다. ThreadPool 스레드는 주 스레드가 종료된 경우 응용 프로그램 실행을 유지하지 않는 배경 스레드이므로 예제의 주 스레드는 콜백이 완료될 때까지 충분히 대기해야 합니다.

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.
 */
표시:
© 2016 Microsoft