동기 메서드를 비동기 방식으로 호출

.NET에서는 모든 메서드를 비동기 방식으로 호출할 수 있습니다. 해당 작업을 수행하려면 호출하려는 메서드와 동일한 시그니처를 사용하여 대리자를 정의합니다. 공용 언어 런타임은 적절한 시그니처를 사용하여 이 대리자에 대한 BeginInvokeEndInvoke 메서드를 자동으로 정의합니다.

참고 항목

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

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

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

참고 항목

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

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

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

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

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

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

Important

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

테스트 메서드 및 비동기 대리자 정의

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

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

using namespace System;
using namespace System::Threading;
using namespace System::Runtime::InteropServices; 

namespace Examples {
namespace AdvancedProgramming {
namespace AsynchronousOperations
{
    public ref class AsyncDemo 
    {
    public:
        // The method to be executed asynchronously.
        String^ TestMethod(int callDuration, [OutAttribute] int% threadId) 
        {
            Console::WriteLine("Test method begins.");
            Thread::Sleep(callDuration);
            threadId = Thread::CurrentThread->ManagedThreadId;
            return String::Format("My call time was {0}.", callDuration);
        }
    };

    // The delegate must have the same signature as the method
    // it will call asynchronously.
    public delegate String^ AsyncMethodCaller(int callDuration, [OutAttribute] int% threadId);
}}}
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);
}
Imports System.Threading
Imports System.Runtime.InteropServices

Namespace Examples.AdvancedProgramming.AsynchronousOperations
    Public Class AsyncDemo
        ' The method to be executed asynchronously.
        Public Function TestMethod(ByVal callDuration As Integer, _
                <Out> ByRef threadId As Integer) As String
            Console.WriteLine("Test method begins.")
            Thread.Sleep(callDuration)
            threadId = Thread.CurrentThread.ManagedThreadId()
            return String.Format("My call time was {0}.", callDuration.ToString())
        End Function
    End Class

    ' The delegate must have the same signature as the method
    ' it will call asynchronously.
    Public Delegate Function AsyncMethodCaller(ByVal callDuration As Integer, _
        <Out> ByRef threadId As Integer) As String
End Namespace

EndInvoke로 비동기 호출 대기

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

Important

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

#using <TestMethod.dll>

using namespace System;
using namespace System::Threading;
using namespace Examples::AdvancedProgramming::AsynchronousOperations;

void main() 
{
    // The asynchronous method puts the thread id here.
    int threadId = 2546;

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

    // Create the delegate.
    AsyncMethodCaller^ caller = gcnew AsyncMethodCaller(ad, &AsyncDemo::TestMethod);
       
    // Initiate the asynchronous call.
    IAsyncResult^ result = caller->BeginInvoke(3000, 
        threadId, nullptr, nullptr);

    Thread::Sleep(1);
    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(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.".
 */
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 asynchronous 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.".
 */
Imports System.Threading
Imports System.Runtime.InteropServices

Namespace Examples.AdvancedProgramming.AsynchronousOperations
    Public Class AsyncMain
        Shared Sub Main()
            ' The asynchronous method puts the thread id here.
            Dim threadId As Integer

            ' Create an instance of the test class.
            Dim ad As New AsyncDemo()

            ' Create the delegate.
            Dim caller As New AsyncMethodCaller(AddressOf ad.TestMethod)

            ' Initiate the asynchronous call.
            Dim result As IAsyncResult = caller.BeginInvoke(3000, _
                threadId, Nothing, Nothing)

            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.
            Dim returnValue As String = caller.EndInvoke(threadId, result)

            Console.WriteLine("The call executed on thread {0}, with return value ""{1}"".", _
                threadId, returnValue)
        End Sub
    End Class

End Namespace

'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.".

WaitHandle로 비동기 호출 대기

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

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

참고 항목

대기 핸들은 EndInvoke를 호출할 때 자동으로 닫히지 않습니다. 대기 핸들에 대한 모든 참조를 해제하면 가비지 수집에서 대기 핸들을 회수할 때 시스템 리소스가 확보됩니다. 대기 핸들 사용을 마친 후 시스템 리소스를 즉시 확보하려면 WaitHandle.Close 메서드를 호출하여 핸들을 삭제합니다. 삭제 가능한 개체를 명시적으로 삭제하면 가비지 컬렉션의 효율성이 향상됩니다.

#using <TestMethod.dll>

using namespace System;
using namespace System::Threading;
using namespace Examples::AdvancedProgramming::AsynchronousOperations;

void main() 
{
    // The asynchronous method puts the thread id here.
    int threadId;

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

    // Create the delegate.
    AsyncMethodCaller^ caller = gcnew AsyncMethodCaller(ad, &AsyncDemo::TestMethod);
       
    // Initiate the asynchronous call.
    IAsyncResult^ result = caller->BeginInvoke(3000, 
        threadId, nullptr, nullptr);

    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(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.".
 */
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 asynchronous 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.".
 */
Imports System.Threading
Imports System.Runtime.InteropServices

Namespace Examples.AdvancedProgramming.AsynchronousOperations

    Public Class AsyncMain
        Shared Sub Main()
            ' The asynchronous method puts the thread id here.
            Dim threadId As Integer

            ' Create an instance of the test class.
            Dim ad As New AsyncDemo()

            ' Create the delegate.
            Dim caller As New AsyncMethodCaller(AddressOf ad.TestMethod)

            ' Initiate the asynchronous call.
            Dim result As IAsyncResult = caller.BeginInvoke(3000, _
                threadId, Nothing, Nothing)

            Thread.Sleep(0)
            Console.WriteLine("Main thread {0} does some work.", _
                Thread.CurrentThread.ManagedThreadId)
            ' Perform additional processing here and then
            ' wait for the WaitHandle to be signaled.
            result.AsyncWaitHandle.WaitOne()

            ' Call EndInvoke to retrieve the results.
            Dim returnValue As String = caller.EndInvoke(threadId, result)

            ' Close the wait handle.
            result.AsyncWaitHandle.Close()

            Console.WriteLine("The call executed on thread {0}, with return value ""{1}"".", _
                threadId, returnValue)
        End Sub
    End Class
End Namespace

'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.".

비동기 호출 완료에 대한 폴링

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

#using <TestMethod.dll>

using namespace System;
using namespace System::Threading;
using namespace Examples::AdvancedProgramming::AsynchronousOperations;

void main() 
{
    // The asynchronous method puts the thread id here.
    int threadId;

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

    // Create the delegate.
    AsyncMethodCaller^ caller = gcnew AsyncMethodCaller(ad, &AsyncDemo::TestMethod);
       
    // Initiate the asynchronous call.
    IAsyncResult^ result = caller->BeginInvoke(3000, 
        threadId, nullptr, nullptr);

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

    // Call EndInvoke to retrieve the results.
    String^ returnValue = caller->EndInvoke(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.".
 */
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 asynchronous 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.".
 */
Imports System.Threading
Imports System.Runtime.InteropServices

Namespace Examples.AdvancedProgramming.AsynchronousOperations

    Public Class AsyncMain
        Shared Sub Main()
            ' The asynchronous method puts the thread id here.
            Dim threadId As Integer

            ' Create an instance of the test class.
            Dim ad As New AsyncDemo()

            ' Create the delegate.
            Dim caller As New AsyncMethodCaller(AddressOf ad.TestMethod)

            ' Initiate the asynchronous call.
            Dim result As IAsyncResult = caller.BeginInvoke(3000, _
                threadId, Nothing, Nothing)

            ' Poll while simulating work.
            While result.IsCompleted = False
                Thread.Sleep(250)
                Console.Write(".")
            End While

            ' Call EndInvoke to retrieve the results.
            Dim returnValue As String = caller.EndInvoke(threadId, result)

            Console.WriteLine(vbCrLf & _
                "The call executed on thread {0}, with return value ""{1}"".", _
                threadId, returnValue)
        End Sub
    End Class
End Namespace

' 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를 호출할 수 있습니다.

예제 관련 참고 사항:

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

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

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

#using <TestMethod.dll>

using namespace System;
using namespace System::Threading;
using namespace System::Runtime::Remoting::Messaging;
using namespace Examples::AdvancedProgramming::AsynchronousOperations;

// The callback method must have the same signature as the
// AsyncCallback delegate.
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(threadId, ar);

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

void main() 
{
    // Create an instance of the test class.
    AsyncDemo^ ad = gcnew AsyncDemo();

    // Create the delegate.
    AsyncMethodCaller^ caller = gcnew AsyncMethodCaller(ad, &AsyncDemo::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,
        dummy, 
        gcnew 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.");
}

/* 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.
 */
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.
 */
Imports System.Threading
Imports System.Runtime.Remoting.Messaging

Namespace Examples.AdvancedProgramming.AsynchronousOperations

    Public Class AsyncMain

        Shared Sub Main()

            ' Create an instance of the test class.
            Dim ad As New AsyncDemo()

            ' Create the delegate.
            Dim caller As New AsyncMethodCaller(AddressOf 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 ByRef parameter, it would have to be a class-
            ' level field so that it could be passed to both BeginInvoke and 
            ' EndInvoke.
            Dim dummy As Integer = 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.
            Dim result As IAsyncResult = caller.BeginInvoke(3000, _
                dummy, _
                AddressOf 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.")
        End Sub

        ' The callback method must have the same signature as the
        ' AsyncCallback delegate.
        Shared Sub CallbackMethod(ByVal ar As IAsyncResult)
            ' Retrieve the delegate.
            Dim result As AsyncResult = CType(ar, AsyncResult)
            Dim caller As AsyncMethodCaller = CType(result.AsyncDelegate, AsyncMethodCaller)

            ' Retrieve the format string that was passed as state 
            ' information.
            Dim formatString As String = CType(ar.AsyncState, String)

            ' Define a variable to receive the value of the <Out> parameter.
            ' If the parameter were ByRef rather than <Out> then it would have to
            ' be a class-level field so it could also be passed to BeginInvoke.
            Dim threadId As Integer = 0

            ' Call EndInvoke to retrieve the results.
            Dim returnValue As String = caller.EndInvoke(threadId, ar)

            ' Use the format string to format the output message.
            Console.WriteLine(formatString, threadId, returnValue)
        End Sub
    End Class
End Namespace

' 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.

참고 항목