방법: 이벤트 기반 비동기 패턴을 지원하는 구성 요소 구현How to: Implement a Component That Supports the Event-based Asynchronous Pattern
상당한 지연을 일으킬 수 있는 몇 가지 작업을 사용하여 클래스를 작성하는 경우 이벤트 기반 비동기 패턴 개요를 구현하여 비동기 기능을 부여하는 것을 고려할 수 있습니다.If you are writing a class with some operations that may incur noticeable delays, consider giving it asynchronous functionality by implementing the Event-based Asynchronous Pattern Overview.
이 연습에서는 이벤트 기반 비동기 패턴을 구현하는 구성 요소를 만드는 방법을 보여줍니다.This walkthrough illustrates how to create a component that implements the Event-based Asynchronous Pattern. ASP.NET, 콘솔 애플리케이션 및 Windows Forms 애플리케이션을 포함한 모든 애플리케이션 모델에서 구성 요소가 올바르게 작동하도록 하는 System.ComponentModel 네임스페이스의 도우미 클래스를 사용하는 것이 좋습니다.It is implemented using helper classes from the System.ComponentModel namespace, which ensures that the component works correctly under any application model, including ASP.NET, Console applications and Windows Forms applications. 이 구성 요소도 PropertyGrid 컨트롤과 고유한 사용자 지정 디자이너를 사용하여 디자인할 수 있습니다.This component is also designable with a PropertyGrid control and your own custom designers.
완료하면 소수를 비동기적으로 계산하는 애플리케이션이 생깁니다.When you are through, you will have an application that computes prime numbers asynchronously. 애플리케이션에는 기본 UI(사용자 인터페이스) 스레드 및 각 소수는 계산을 위한 스레드가 있습니다.Your application will have a main user interface (UI) thread and a thread for each prime number calculation. 큰 숫자가 소수인지 테스트하는 데는 상당한 시간이 걸릴 수도 있지만 이 지연으로 기본 UI 스레드가 중단되지 않으며 계산 중에도 폼이 빠르게 응답하게 됩니다.Although testing whether a large number is prime can take a noticeable amount of time, the main UI thread will not be interrupted by this delay, and the form will be responsive during the calculations. 원하는 계산 수만큼 동시에 또는 선택적으로 취소 보류 계산을 실행할 수 있습니다.You will be able to run as many calculations as you like concurrently and selectively cancel pending calculations.
이 연습에서 설명하는 작업은 다음과 같습니다.Tasks illustrated in this walkthrough include:
구성 요소 만들기Creating the Component
공용 비동기 이벤트 및 대리자 정의Defining Public Asynchronous Events and Delegates
전용 대리자 정의Defining Private Delegates
공용 이벤트 구현Implementing Public Events
완료 메서드 구현Implementing the Completion Method
작업자 메서드 구현Implementing the Worker Methods
시작 및 취소 메서드 구현Implementing Start and Cancel Methods
이 항목의 코드를 단일 목록으로 복사하려면 How to: Implement a Client of the Event-based Asynchronous Pattern(방법: 이벤트 기반 비동기 패턴의 클라이언트 구현)을 참조하세요.To copy the code in this topic as a single listing, see How to: Implement a Client of the Event-based Asynchronous Pattern.
구성 요소 만들기Creating the Component
첫 번째 단계는 이벤트 기반 비동기 패턴을 구현하는 구성 요소를 만드는 것입니다.The first step is to create the component that will implement the Event-based Asynchronous Pattern.
구성 요소를 만들려면To create the component
- Component에서 상속되는
PrimeNumberCalculator
라는 클래스를 만듭니다.Create a class calledPrimeNumberCalculator
that inherits from Component.
공용 비동기 이벤트 및 대리자 정의Defining Public Asynchronous Events and Delegates
구성 요소는 이벤트를 사용하는 클라이언트와 통신합니다.Your component communicates to clients using events. MethodNameCompleted 이벤트는 클라이언트에 비동기 작업의 완료를 알리고 MethodNameProgressChanged 이벤트는 클라이언트에 비동기 작업의 진행률을 알립니다.The MethodNameCompleted event alerts clients to the completion of an asynchronous task, and the MethodNameProgressChanged event informs clients of the progress of an asynchronous task.
구성 요소의 클라이언트에 대한 비동기 이벤트를 정의하려면:To define asynchronous events for clients of your component:
System.Threading 및 System.Collections.Specialized 네임스페이스를 파일 맨 위로 가져옵니다.Import the System.Threading and System.Collections.Specialized namespaces at the top of your file.
using System; using System.Collections; using System.Collections.Specialized; using System.ComponentModel; using System.Data; using System.Drawing; using System.Globalization; using System.Threading; using System.Windows.Forms;
Imports System.Collections Imports System.Collections.Specialized Imports System.ComponentModel Imports System.Drawing Imports System.Globalization Imports System.Threading Imports System.Windows.Forms
PrimeNumberCalculator
클래스 정의 전에 진행률 및 완료 이벤트에 대한 대리자를 선언합니다.Before thePrimeNumberCalculator
class definition, declare delegates for progress and completion events.public delegate void ProgressChangedEventHandler( ProgressChangedEventArgs e); public delegate void CalculatePrimeCompletedEventHandler( object sender, CalculatePrimeCompletedEventArgs e);
Public Delegate Sub ProgressChangedEventHandler( _ ByVal e As ProgressChangedEventArgs) Public Delegate Sub CalculatePrimeCompletedEventHandler( _ ByVal sender As Object, _ ByVal e As CalculatePrimeCompletedEventArgs)
PrimeNumberCalculator
클래스 정의에서 클라이언트에 진행률 및 완료를 보고하는 이벤트를 선언합니다.In thePrimeNumberCalculator
class definition, declare events for reporting progress and completion to clients.public event ProgressChangedEventHandler ProgressChanged; public event CalculatePrimeCompletedEventHandler CalculatePrimeCompleted;
Public Event ProgressChanged _ As ProgressChangedEventHandler Public Event CalculatePrimeCompleted _ As CalculatePrimeCompletedEventHandler
PrimeNumberCalculator
클래스 정의 후 각 계산의 결과를CalculatePrimeCompleted
.event에 대한 클라이언트 이벤트 처리기에 보고하기 위해CalculatePrimeCompletedEventArgs
클래스를 파생시킵니다.After thePrimeNumberCalculator
class definition, derive theCalculatePrimeCompletedEventArgs
class for reporting the outcome of each calculation to the client's event handler for theCalculatePrimeCompleted
.event.AsyncCompletedEventArgs
속성 외에도 이 클래스를 사용하면 클라이언트가 테스트된 숫자, 숫자가 소수인지 여부 및 소수가 아닌 경우 첫 번째 제수를 확인할 수 있습니다.In addition to theAsyncCompletedEventArgs
properties, this class enables the client to determine what number was tested, whether it is prime, and what the first divisor is if it is not prime.public class CalculatePrimeCompletedEventArgs : AsyncCompletedEventArgs { private int numberToTestValue = 0; private int firstDivisorValue = 1; private bool isPrimeValue; public CalculatePrimeCompletedEventArgs( int numberToTest, int firstDivisor, bool isPrime, Exception e, bool canceled, object state) : base(e, canceled, state) { this.numberToTestValue = numberToTest; this.firstDivisorValue = firstDivisor; this.isPrimeValue = isPrime; } public int NumberToTest { get { // Raise an exception if the operation failed or // was canceled. RaiseExceptionIfNecessary(); // If the operation was successful, return the // property value. return numberToTestValue; } } public int FirstDivisor { get { // Raise an exception if the operation failed or // was canceled. RaiseExceptionIfNecessary(); // If the operation was successful, return the // property value. return firstDivisorValue; } } public bool IsPrime { get { // Raise an exception if the operation failed or // was canceled. RaiseExceptionIfNecessary(); // If the operation was successful, return the // property value. return isPrimeValue; } } }
Public Class CalculatePrimeCompletedEventArgs Inherits AsyncCompletedEventArgs Private numberToTestValue As Integer = 0 Private firstDivisorValue As Integer = 1 Private isPrimeValue As Boolean Public Sub New( _ ByVal numberToTest As Integer, _ ByVal firstDivisor As Integer, _ ByVal isPrime As Boolean, _ ByVal e As Exception, _ ByVal canceled As Boolean, _ ByVal state As Object) MyBase.New(e, canceled, state) Me.numberToTestValue = numberToTest Me.firstDivisorValue = firstDivisor Me.isPrimeValue = isPrime End Sub Public ReadOnly Property NumberToTest() As Integer Get ' Raise an exception if the operation failed ' or was canceled. RaiseExceptionIfNecessary() ' If the operation was successful, return ' the property value. Return numberToTestValue End Get End Property Public ReadOnly Property FirstDivisor() As Integer Get ' Raise an exception if the operation failed ' or was canceled. RaiseExceptionIfNecessary() ' If the operation was successful, return ' the property value. Return firstDivisorValue End Get End Property Public ReadOnly Property IsPrime() As Boolean Get ' Raise an exception if the operation failed ' or was canceled. RaiseExceptionIfNecessary() ' If the operation was successful, return ' the property value. Return isPrimeValue End Get End Property End Class
검사점Checkpoint
이때 구성 요소를 빌드할 수 있습니다.At this point, you can build the component.
구성 요소를 테스트하려면To test your component
구성 요소를 컴파일합니다.Compile the component.
두 개의 컴파일러 경고를 받게 됩니다.You will receive two compiler warnings:
warning CS0067: The event 'AsynchronousPatternExample.PrimeNumberCalculator.ProgressChanged' is never used warning CS0067: The event 'AsynchronousPatternExample.PrimeNumberCalculator.CalculatePrimeCompleted' is never used
이러한 경고는 다음 섹션에서 지워집니다.These warnings will be cleared in the next section.
전용 대리자 정의Defining Private Delegates
PrimeNumberCalculator
구성 요소의 비동기 측면은 SendOrPostCallback으로 알려진 특수 대리자를 사용하여 내부적으로 구현됩니다.The asynchronous aspects of the PrimeNumberCalculator
component are implemented internally with a special delegate known as a SendOrPostCallback. SendOrPostCallback은 ThreadPool 스레드에서 실행되는 콜백 메서드를 나타냅니다.A SendOrPostCallback represents a callback method that executes on a ThreadPool thread. 콜백 메서드에는 Object 형식의 단일 매개 변수를 사용하는 시그니처가 있어야 합니다. 즉, 래퍼 클래스의 대리자 간에 상태를 전달해야 합니다.The callback method must have a signature that takes a single parameter of type Object, which means you will need to pass state among delegates in a wrapper class. 자세한 내용은 SendOrPostCallback을 참조하세요.For more information, see SendOrPostCallback.
구성 요소의 내부 비동기 동작을 구현하려면:To implement your component's internal asynchronous behavior:
PrimeNumberCalculator
클래스에 SendOrPostCallback 대리자를 선언하고 만듭니다.Declare and create the SendOrPostCallback delegates in thePrimeNumberCalculator
class.InitializeDelegates
라는 유틸리티 메서드에서 SendOrPostCallback 개체를 만듭니다.Create the SendOrPostCallback objects in a utility method calledInitializeDelegates
.각각 클라이언트에 진행률 및 완료를 보고하는 두 개의 대리자가 필요합니다.You will need two delegates: one for reporting progress to the client, and one for reporting completion to the client.
private SendOrPostCallback onProgressReportDelegate; private SendOrPostCallback onCompletedDelegate;
Private onProgressReportDelegate As SendOrPostCallback Private onCompletedDelegate As SendOrPostCallback
protected virtual void InitializeDelegates() { onProgressReportDelegate = new SendOrPostCallback(ReportProgress); onCompletedDelegate = new SendOrPostCallback(CalculateCompleted); }
Protected Overridable Sub InitializeDelegates() onProgressReportDelegate = _ New SendOrPostCallback(AddressOf ReportProgress) onCompletedDelegate = _ New SendOrPostCallback(AddressOf CalculateCompleted) End Sub
구성 요소의 생성자에서
InitializeDelegates
메서드를 호출합니다.Call theInitializeDelegates
method in your component's constructor.public PrimeNumberCalculator() { InitializeComponent(); InitializeDelegates(); }
Public Sub New() InitializeComponent() InitializeDelegates() End Sub
PrimeNumberCalculator
클래스에서 실제 작업이 비동기적으로 수행되도록 처리하는 대리자를 선언합니다.Declare a delegate in thePrimeNumberCalculator
class that handles the actual work to be done asynchronously. 이 대리자는 숫자가 소수인지 테스트하는 작업자 메서드를 래핑합니다.This delegate wraps the worker method that tests whether a number is prime. 대리자는 비동기 작업의 수명을 추적하는 데 사용되는 AsyncOperation 매개 변수를 사용합니다.The delegate takes an AsyncOperation parameter, which will be used to track the lifetime of the asynchronous operation.private delegate void WorkerEventHandler( int numberToCheck, AsyncOperation asyncOp);
Private Delegate Sub WorkerEventHandler( _ ByVal numberToCheck As Integer, _ ByVal asyncOp As AsyncOperation)
보류 중인 비동기 작업의 수명을 관리하기 위한 컬렉션을 만듭니다.Create a collection for managing lifetimes of pending asynchronous operations. 클라이언트에는 실행 및 완료 시 작업을 추적하는 방법이 필요하며, 이 추적을 수행하려면 클라이언트가 비동기 메서드를 호출할 때 고유한 토큰 또는 작업 ID를 전달해야 합니다.The client needs a way to track operations as they are executed and completed, and this tracking is done by requiring the client to pass a unique token, or task ID, when the client makes the call to the asynchronous method.
PrimeNumberCalculator
구성 요소는 작업 ID를 해당 호출과 연결하여 각 호출을 추적해야 합니다.ThePrimeNumberCalculator
component must keep track of each call by associating the task ID with its corresponding invocation. 클라이언트가 고유하지 않은 작업 ID를 전달하면PrimeNumberCalculator
구성 요소가 예외를 발생시켜야 합니다.If the client passes a task ID that is not unique, thePrimeNumberCalculator
component must raise an exception.PrimeNumberCalculator
구성 요소는 HybridDictionary라는 특수 컬렉션 클래스를 사용하여 작업 ID를 추적합니다.ThePrimeNumberCalculator
component keeps track of task ID by using a special collection class called a HybridDictionary. 클래스 정의에서userTokenToLifetime
이라는 HybridDictionary를 만듭니다.In the class definition, create a HybridDictionary calleduserTokenToLifetime
.private HybridDictionary userStateToLifetime = new HybridDictionary();
Private userStateToLifetime As New HybridDictionary()
공용 이벤트 구현Implementing Public Events
이벤트 기반 비동기 패턴을 구현하는 구성 요소는 이벤트를 사용하는 클라이언트와 통신합니다.Components that implement the Event-based Asynchronous Pattern communicate to clients using events. 이러한 이벤트는 AsyncOperation 클래스를 통해 적절한 스레드에서 호출됩니다.These events are invoked on the proper thread with the help of the AsyncOperation class.
구성 요소의 클라이언트에 이벤트를 발생시키려면:To raise events to your component's clients:
클라이언트에 보고할 공용 이벤트를 구현합니다.Implement public events for reporting to clients. 각각 진행률 및 완료를 보고하기 위한 두 개의 이벤트가 필요합니다.You will need an event for reporting progress and one for reporting completion.
// This method is invoked via the AsyncOperation object, // so it is guaranteed to be executed on the correct thread. private void CalculateCompleted(object operationState) { CalculatePrimeCompletedEventArgs e = operationState as CalculatePrimeCompletedEventArgs; OnCalculatePrimeCompleted(e); } // This method is invoked via the AsyncOperation object, // so it is guaranteed to be executed on the correct thread. private void ReportProgress(object state) { ProgressChangedEventArgs e = state as ProgressChangedEventArgs; OnProgressChanged(e); } protected void OnCalculatePrimeCompleted( CalculatePrimeCompletedEventArgs e) { if (CalculatePrimeCompleted != null) { CalculatePrimeCompleted(this, e); } } protected void OnProgressChanged(ProgressChangedEventArgs e) { if (ProgressChanged != null) { ProgressChanged(e); } }
' This method is invoked via the AsyncOperation object, ' so it is guaranteed to be executed on the correct thread. Private Sub CalculateCompleted(ByVal operationState As Object) Dim e As CalculatePrimeCompletedEventArgs = operationState OnCalculatePrimeCompleted(e) End Sub ' This method is invoked via the AsyncOperation object, ' so it is guaranteed to be executed on the correct thread. Private Sub ReportProgress(ByVal state As Object) Dim e As ProgressChangedEventArgs = state OnProgressChanged(e) End Sub Protected Sub OnCalculatePrimeCompleted( _ ByVal e As CalculatePrimeCompletedEventArgs) RaiseEvent CalculatePrimeCompleted(Me, e) End Sub Protected Sub OnProgressChanged( _ ByVal e As ProgressChangedEventArgs) RaiseEvent ProgressChanged(e) End Sub
완료 메서드 구현Implementing the Completion Method
완료 대리자는 비동기 작업이 성공적인 완료, 오류 또는 취소로 종료될 경우 기본적인 자유 스레드 비동기 동작이 호출되는 메서드입니다.The completion delegate is the method that the underlying, free-threaded asynchronous behavior will invoke when the asynchronous operation ends by successful completion, error, or cancellation. 이 호출은 임의 스레드에서 발생합니다.This invocation happens on an arbitrary thread.
이 메서드는 클라이언트의 작업 ID가 고유한 클라이언트 토큰의 내부 컬렉션에서 제거되는 위치입니다.This method is where the client's task ID is removed from the internal collection of unique client tokens. 또한 이 메서드는 해당 AsyncOperation에서 PostOperationCompleted 메서드를 호출하여 특정 비동기 작업의 수명을 종료합니다.This method also ends the lifetime of a particular asynchronous operation by calling the PostOperationCompleted method on the corresponding AsyncOperation. 이 호출은 애플리케이션 모델에 적절한 스레드에서 완료 이벤트를 발생시킵니다.This call raises the completion event on the thread that is appropriate for the application model. PostOperationCompleted 메서드를 호출한 후에는 AsyncOperation의 이 인스턴스를 더 이상 사용할 수 없고 이후에 이를 사용하려고 시도하면 예외가 throw됩니다.After the PostOperationCompleted method is called, this instance of AsyncOperation can no longer be used, and any subsequent attempts to use it will throw an exception.
CompletionMethod
시그니처는 비동기 작업의 결과를 설명하는 데 필요한 모든 상태를 포함해야 합니다.The CompletionMethod
signature must hold all state necessary to describe the outcome of the asynchronous operation. 이 시그니처는 이 특정 비동기 작업으로 테스트된 숫자의 상태, 숫자가 소수인지 여부 및 소수가 아닌 경우 첫 번째 제수의 값을 포함합니다.It holds state for the number that was tested by this particular asynchronous operation, whether the number is prime, and the value of its first divisor if it is not a prime number. 또한 발생한 예외를 설명하는 상태 및 이 특정 작업에 해당하는 AsyncOperation을 포함합니다.It also holds state describing any exception that occurred, and the AsyncOperation corresponding to this particular task.
비동기 작업을 완료하려면:To complete an asynchronous operation:
완료 메서드를 구현합니다.Implement the completion method. 이 메서드는 클라이언트의
CalculatePrimeCompletedEventHandler
를 통해 클라이언트로 반환되는CalculatePrimeCompletedEventArgs
를 채우는 데 사용할 6개의 매개 변수를 사용합니다.It takes six parameters, which it uses to populate aCalculatePrimeCompletedEventArgs
that is returned to the client through the client'sCalculatePrimeCompletedEventHandler
. 이 메서드는 내부 컬렉션에서 클라이언트의 작업 ID 토큰을 제거하고 PostOperationCompleted 호출을 통해 비동기 작업 수명을 종료합니다.It removes the client's task ID token from the internal collection, and it ends the asynchronous operation's lifetime with a call to PostOperationCompleted. AsyncOperation은 애플리케이션 모델에 적합한 스레드 또는 컨텍스트에 대한 호출을 마샬링합니다.The AsyncOperation marshals the call to the thread or context that is appropriate for the application model.// This is the method that the underlying, free-threaded // asynchronous behavior will invoke. This will happen on // an arbitrary thread. private void CompletionMethod( int numberToTest, int firstDivisor, bool isPrime, Exception exception, bool canceled, AsyncOperation asyncOp ) { // If the task was not previously canceled, // remove the task from the lifetime collection. if (!canceled) { lock (userStateToLifetime.SyncRoot) { userStateToLifetime.Remove(asyncOp.UserSuppliedState); } } // Package the results of the operation in a // CalculatePrimeCompletedEventArgs. CalculatePrimeCompletedEventArgs e = new CalculatePrimeCompletedEventArgs( numberToTest, firstDivisor, isPrime, exception, canceled, asyncOp.UserSuppliedState); // End the task. The asyncOp object is responsible // for marshaling the call. asyncOp.PostOperationCompleted(onCompletedDelegate, e); // Note that after the call to OperationCompleted, // asyncOp is no longer usable, and any attempt to use it // will cause an exception to be thrown. }
' This is the method that the underlying, free-threaded ' asynchronous behavior will invoke. This will happen on ' an arbitrary thread. Private Sub CompletionMethod( _ ByVal numberToTest As Integer, _ ByVal firstDivisor As Integer, _ ByVal prime As Boolean, _ ByVal exc As Exception, _ ByVal canceled As Boolean, _ ByVal asyncOp As AsyncOperation) ' If the task was not previously canceled, ' remove the task from the lifetime collection. If Not canceled Then SyncLock userStateToLifetime.SyncRoot userStateToLifetime.Remove(asyncOp.UserSuppliedState) End SyncLock End If ' Package the results of the operation in a ' CalculatePrimeCompletedEventArgs. Dim e As New CalculatePrimeCompletedEventArgs( _ numberToTest, _ firstDivisor, _ prime, _ exc, _ canceled, _ asyncOp.UserSuppliedState) ' End the task. The asyncOp object is responsible ' for marshaling the call. asyncOp.PostOperationCompleted(onCompletedDelegate, e) ' Note that after the call to PostOperationCompleted, asyncOp ' is no longer usable, and any attempt to use it will cause. ' an exception to be thrown. End Sub
검사점Checkpoint
이때 구성 요소를 빌드할 수 있습니다.At this point, you can build the component.
구성 요소를 테스트하려면To test your component
구성 요소를 컴파일합니다.Compile the component.
하나의 컴파일러 경고를 받게 됩니다.You will receive one compiler warning:
warning CS0169: The private field 'AsynchronousPatternExample.PrimeNumberCalculator.workerDelegate' is never used
이 경고는 다음 섹션에서 해결됩니다.This warning will be resolved in the next section.
작업자 메서드 구현Implementing the Worker Methods
지금까지 PrimeNumberCalculator
구성 요소에 대해 지원되는 비동기 코드를 구현했습니다.So far, you have implemented the supporting asynchronous code for the PrimeNumberCalculator
component. 이제 실제 작업을 수행하는 코드를 구현할 수 있습니다.Now you can implement the code that does the actual work. 세 가지 메서드 CalculateWorker
, BuildPrimeNumberList
및 IsPrime
을 구현합니다.You will implement three methods: CalculateWorker
, BuildPrimeNumberList
, and IsPrime
. BuildPrimeNumberList
및 IsPrime
은 함께 테스트 숫자의 제곱근까지 모든 소수를 찾아서 숫자가 소수인지 확인하는 에라토스테네스의 체라는 잘 알려진 알고리즘을 구성합니다.Together, BuildPrimeNumberList
and IsPrime
comprise a well-known algorithm called the Sieve of Eratosthenes, which determines if a number is prime by finding all the prime numbers up to the square root of the test number. 해당 지점에서 제수를 찾을 수 없으면 테스트 숫자는 소수입니다.If no divisors are found by that point, the test number is prime.
효율성 최대화를 위해 이 구성 요소를 작성한 경우 이 구성 요소는 여러 테스트 숫자에 대한 다양한 호출을 통해 검색된 모든 소수를 기억합니다.If this component were written for maximum efficiency, it would remember all the prime numbers discovered by various invocations for different test numbers. 2, 3, 5와 같은 사소한 제수도 확인합니다.It would also check for trivial divisors like 2, 3, and 5. 그러나 이 예제의 목적은 시간이 오래 걸리는 작업을 비동기적으로 실행하는 방법을 보여주는 것이므로 이러한 최적화는 연습용으로 유지됩니다.The intent of this example is to demonstrate how time-consuming operations can be executed asynchronously, however, so these optimizations are left as an exercise for you.
CalculateWorker
메서드는 대리자로 래핑되고 BeginInvoke
호출을 통해 비동기적으로 호출됩니다.The CalculateWorker
method is wrapped in a delegate and is invoked asynchronously with a call to BeginInvoke
.
참고
진행률 보고는 BuildPrimeNumberList
메서드에서 구현됩니다.Progress reporting is implemented in the BuildPrimeNumberList
method. 빠른 컴퓨터에서는 ProgressChanged
이벤트가 연속적으로 발생할 수 있습니다.On fast computers, ProgressChanged
events can be raised in rapid succession. 이러한 이벤트가 발생하는 클라이언트 스레드에서 이 상황을 처리할 수 있어야 합니다.The client thread, on which these events are raised, must be able to handle this situation. 사용자 인터페이스 코드가 메시지로 넘쳐 유지할 수 없어서 응답하지 않을 수 있습니다.User interface code may be flooded with messages and unable to keep up, resulting in unresponsiveness. 이 상황을 처리하는 예제 사용자 인터페이스는 방법: 이벤트 기반 비동기 패턴의 클라이언트 구현을 참조하세요.For an example user interface that handles this situation, see How to: Implement a Client of the Event-based Asynchronous Pattern.
소수 계산을 비동기적으로 실행하려면:To execute the prime number calculation asynchronously:
TaskCanceled
유틸리티 메서드를 구현합니다.Implement theTaskCanceled
utility method. 이 메서드는 지정된 작업 ID의 작업 수명 컬렉션을 확인하고 작업 ID를 찾을 수 없는 경우true
를 반환합니다.This checks the task lifetime collection for the given task ID, and returnstrue
if the task ID is not found.// Utility method for determining if a // task has been canceled. private bool TaskCanceled(object taskId) { return( userStateToLifetime[taskId] == null ); }
' Utility method for determining if a ' task has been canceled. Private Function TaskCanceled(ByVal taskId As Object) As Boolean Return (userStateToLifetime(taskId) Is Nothing) End Function
CalculateWorker
메서드를 구현합니다.Implement theCalculateWorker
method. 이 메서드는 두 개의 매개 변수인 테스트할 숫자 및 AsyncOperation을 사용합니다.It takes two parameters: a number to test, and an AsyncOperation.// This method performs the actual prime number computation. // It is executed on the worker thread. private void CalculateWorker( int numberToTest, AsyncOperation asyncOp) { bool isPrime = false; int firstDivisor = 1; Exception e = null; // Check that the task is still active. // The operation may have been canceled before // the thread was scheduled. if (!TaskCanceled(asyncOp.UserSuppliedState)) { try { // Find all the prime numbers up to // the square root of numberToTest. ArrayList primes = BuildPrimeNumberList( numberToTest, asyncOp); // Now we have a list of primes less than // numberToTest. isPrime = IsPrime( primes, numberToTest, out firstDivisor); } catch (Exception ex) { e = ex; } } //CalculatePrimeState calcState = new CalculatePrimeState( // numberToTest, // firstDivisor, // isPrime, // e, // TaskCanceled(asyncOp.UserSuppliedState), // asyncOp); //this.CompletionMethod(calcState); this.CompletionMethod( numberToTest, firstDivisor, isPrime, e, TaskCanceled(asyncOp.UserSuppliedState), asyncOp); //completionMethodDelegate(calcState); }
' This method performs the actual prime number computation. ' It is executed on the worker thread. Private Sub CalculateWorker( _ ByVal numberToTest As Integer, _ ByVal asyncOp As AsyncOperation) Dim prime As Boolean = False Dim firstDivisor As Integer = 1 Dim exc As Exception = Nothing ' Check that the task is still active. ' The operation may have been canceled before ' the thread was scheduled. If Not Me.TaskCanceled(asyncOp.UserSuppliedState) Then Try ' Find all the prime numbers up to the ' square root of numberToTest. Dim primes As ArrayList = BuildPrimeNumberList( _ numberToTest, asyncOp) ' Now we have a list of primes less than 'numberToTest. prime = IsPrime( _ primes, _ numberToTest, _ firstDivisor) Catch ex As Exception exc = ex End Try End If Me.CompletionMethod( _ numberToTest, _ firstDivisor, _ prime, _ exc, _ TaskCanceled(asyncOp.UserSuppliedState), _ asyncOp) End Sub
BuildPrimeNumberList
를 구현해야 합니다.ImplementBuildPrimeNumberList
. 이 메서드는 두 개의 매개 변수인 테스트할 숫자 및 AsyncOperation을 사용합니다.It takes two parameters: the number to test, and an AsyncOperation. AsyncOperation을 사용하여 진행률 및 증분 결과를 보고합니다.It uses the AsyncOperation to report progress and incremental results. 이 메서드를 사용하면 클라이언트의 이벤트 처리기가 애플리케이션 모델에 대한 적절한 스레드 또는 컨텍스트에서 호출됩니다.This assures that the client's event handlers are called on the proper thread or context for the application model.BuildPrimeNumberList
는 소수를 찾을 경우ProgressChanged
이벤트에 대한 클라이언트 이벤트 처리기에 이 소수를 증분 결과로 보고합니다.WhenBuildPrimeNumberList
finds a prime number, it reports this as an incremental result to the client's event handler for theProgressChanged
event. 이 작업에는LatestPrimeNumber
라는 하나의 추가된 속성을 포함하는CalculatePrimeProgressChangedEventArgs
라는 ProgressChangedEventArgs에서 파생된 클래스가 필요합니다.This requires a class derived from ProgressChangedEventArgs, calledCalculatePrimeProgressChangedEventArgs
, which has one added property calledLatestPrimeNumber
.또한
BuildPrimeNumberList
메서드는TaskCanceled
메서드를 정기적으로 호출하고 메서드가true
를 반환하는 경우 종료됩니다.TheBuildPrimeNumberList
method also periodically calls theTaskCanceled
method and exits if the method returnstrue
.// This method computes the list of prime numbers used by the // IsPrime method. private ArrayList BuildPrimeNumberList( int numberToTest, AsyncOperation asyncOp) { ProgressChangedEventArgs e = null; ArrayList primes = new ArrayList(); int firstDivisor; int n = 5; // Add the first prime numbers. primes.Add(2); primes.Add(3); // Do the work. while (n < numberToTest && !TaskCanceled( asyncOp.UserSuppliedState ) ) { if (IsPrime(primes, n, out firstDivisor)) { // Report to the client that a prime was found. e = new CalculatePrimeProgressChangedEventArgs( n, (int)((float)n / (float)numberToTest * 100), asyncOp.UserSuppliedState); asyncOp.Post(this.onProgressReportDelegate, e); primes.Add(n); // Yield the rest of this time slice. Thread.Sleep(0); } // Skip even numbers. n += 2; } return primes; }
' This method computes the list of prime numbers used by the ' IsPrime method. Private Function BuildPrimeNumberList( _ ByVal numberToTest As Integer, _ ByVal asyncOp As AsyncOperation) As ArrayList Dim e As ProgressChangedEventArgs = Nothing Dim primes As New ArrayList Dim firstDivisor As Integer Dim n As Integer = 5 ' Add the first prime numbers. primes.Add(2) primes.Add(3) ' Do the work. While n < numberToTest And _ Not Me.TaskCanceled(asyncOp.UserSuppliedState) If IsPrime(primes, n, firstDivisor) Then ' Report to the client that you found a prime. e = New CalculatePrimeProgressChangedEventArgs( _ n, _ CSng(n) / CSng(numberToTest) * 100, _ asyncOp.UserSuppliedState) asyncOp.Post(Me.onProgressReportDelegate, e) primes.Add(n) ' Yield the rest of this time slice. Thread.Sleep(0) End If ' Skip even numbers. n += 2 End While Return primes End Function
IsPrime
를 구현해야 합니다.ImplementIsPrime
. 이 메서드는 세 개의 매개 변수인 알려진 소수 목록, 테스트할 숫자 및 발견된 첫 번째 제수의 출력 매개 변수를 사용합니다.It takes three parameters: a list of known prime numbers, the number to test, and an output parameter for the first divisor found. 소수 목록이 제공된 경우 테스트 숫자가 소수인지 확인합니다.Given the list of prime numbers, it determines if the test number is prime.// This method tests n for primality against the list of // prime numbers contained in the primes parameter. private bool IsPrime( ArrayList primes, int n, out int firstDivisor) { bool foundDivisor = false; bool exceedsSquareRoot = false; int i = 0; int divisor = 0; firstDivisor = 1; // Stop the search if: // there are no more primes in the list, // there is a divisor of n in the list, or // there is a prime that is larger than // the square root of n. while ( (i < primes.Count) && !foundDivisor && !exceedsSquareRoot) { // The divisor variable will be the smallest // prime number not yet tried. divisor = (int)primes[i++]; // Determine whether the divisor is greater // than the square root of n. if (divisor * divisor > n) { exceedsSquareRoot = true; } // Determine whether the divisor is a factor of n. else if (n % divisor == 0) { firstDivisor = divisor; foundDivisor = true; } } return !foundDivisor; }
' This method tests n for primality against the list of ' prime numbers contained in the primes parameter. Private Function IsPrime( _ ByVal primes As ArrayList, _ ByVal n As Integer, _ ByRef firstDivisor As Integer) As Boolean Dim foundDivisor As Boolean = False Dim exceedsSquareRoot As Boolean = False Dim i As Integer = 0 Dim divisor As Integer = 0 firstDivisor = 1 ' Stop the search if: ' there are no more primes in the list, ' there is a divisor of n in the list, or ' there is a prime that is larger than ' the square root of n. While i < primes.Count AndAlso _ Not foundDivisor AndAlso _ Not exceedsSquareRoot ' The divisor variable will be the smallest prime number ' not yet tried. divisor = primes(i) i = i + 1 ' Determine whether the divisor is greater than the ' square root of n. If divisor * divisor > n Then exceedsSquareRoot = True ' Determine whether the divisor is a factor of n. ElseIf n Mod divisor = 0 Then firstDivisor = divisor foundDivisor = True End If End While Return Not foundDivisor End Function
ProgressChangedEventArgs에서
CalculatePrimeProgressChangedEventArgs
를 파생시킵니다.DeriveCalculatePrimeProgressChangedEventArgs
from ProgressChangedEventArgs. 이 클래스는ProgressChanged
이벤트에 대한 클라이언트 이벤트 처리기에 증분 결과를 보고하는 데 필요합니다.This class is necessary for reporting incremental results to the client's event handler for theProgressChanged
event.LatestPrimeNumber
라는 하나의 추가된 속성을 포함합니다.It has one added property calledLatestPrimeNumber
.public class CalculatePrimeProgressChangedEventArgs : ProgressChangedEventArgs { private int latestPrimeNumberValue = 1; public CalculatePrimeProgressChangedEventArgs( int latestPrime, int progressPercentage, object userToken) : base( progressPercentage, userToken ) { this.latestPrimeNumberValue = latestPrime; } public int LatestPrimeNumber { get { return latestPrimeNumberValue; } } }
Public Class CalculatePrimeProgressChangedEventArgs Inherits ProgressChangedEventArgs Private latestPrimeNumberValue As Integer = 1 Public Sub New( _ ByVal latestPrime As Integer, _ ByVal progressPercentage As Integer, _ ByVal UserState As Object) MyBase.New(progressPercentage, UserState) Me.latestPrimeNumberValue = latestPrime End Sub Public ReadOnly Property LatestPrimeNumber() As Integer Get Return latestPrimeNumberValue End Get End Property End Class
검사점Checkpoint
이때 구성 요소를 빌드할 수 있습니다.At this point, you can build the component.
구성 요소를 테스트하려면To test your component
구성 요소를 컴파일합니다.Compile the component.
계속해서 비동기 작업을 시작 및 취소하는
CalculatePrimeAsync
및CancelAsync
메서드를 작성합니다.All that remains to be written are the methods to start and cancel asynchronous operations,CalculatePrimeAsync
andCancelAsync
.
시작 및 취소 메서드 구현Implementing the Start and Cancel Methods
메서드를 래핑하는 대리자에서 BeginInvoke
를 호출하여 자체 스레드에서 작업자 메서드를 시작합니다.You start the worker method on its own thread by calling BeginInvoke
on the delegate that wraps it. 특정 비동기 작업의 수명을 관리하려면 AsyncOperationManager 도우미 클래스에서 CreateOperation 메서드를 호출합니다.To manage the lifetime of a particular asynchronous operation, you call the CreateOperation method on the AsyncOperationManager helper class. 이렇게 하면 클라이언트의 이벤트 처리기에 대한 호출을 올바른 스레드 또는 컨텍스트로 마샬링하는 AsyncOperation이 반환됩니다.This returns an AsyncOperation, which marshals calls on the client's event handlers to the proper thread or context.
해당 AsyncOperation에서 PostOperationCompleted를 호출하여 특정 보류 작업을 취소합니다.You cancel a particular pending operation by calling PostOperationCompleted on its corresponding AsyncOperation. 이렇게 하면 해당 작업이 종료되고 이후에 AsyncOperation을 호출하면 예외가 throw됩니다.This ends that operation, and any subsequent calls to its AsyncOperation will throw an exception.
시작 및 취소 기능을 구현하려면:To implement Start and Cancel functionality:
CalculatePrimeAsync
메서드를 구현합니다.Implement theCalculatePrimeAsync
method. 클라이언트 제공 토큰(작업 ID)이 현재 보류 중인 작업을 나타내는 모든 토큰에 관련해서 고유한지 확인합니다.Make sure the client-supplied token (task ID) is unique with respect to all the tokens representing currently pending tasks. 클라이언트가 고유하지 않은 토큰으로 전달되면CalculatePrimeAsync
가 예외를 발생시킵니다.If the client passes in a non-unique token,CalculatePrimeAsync
raises an exception. 그렇지 않으면 토큰이 작업 ID 컬렉션에 추가됩니다.Otherwise, the token is added to the task ID collection.// This method starts an asynchronous calculation. // First, it checks the supplied task ID for uniqueness. // If taskId is unique, it creates a new WorkerEventHandler // and calls its BeginInvoke method to start the calculation. public virtual void CalculatePrimeAsync( int numberToTest, object taskId) { // Create an AsyncOperation for taskId. AsyncOperation asyncOp = AsyncOperationManager.CreateOperation(taskId); // Multiple threads will access the task dictionary, // so it must be locked to serialize access. lock (userStateToLifetime.SyncRoot) { if (userStateToLifetime.Contains(taskId)) { throw new ArgumentException( "Task ID parameter must be unique", "taskId"); } userStateToLifetime[taskId] = asyncOp; } // Start the asynchronous operation. WorkerEventHandler workerDelegate = new WorkerEventHandler(CalculateWorker); workerDelegate.BeginInvoke( numberToTest, asyncOp, null, null); }
' This method starts an asynchronous calculation. ' First, it checks the supplied task ID for uniqueness. ' If taskId is unique, it creates a new WorkerEventHandler ' and calls its BeginInvoke method to start the calculation. Public Overridable Sub CalculatePrimeAsync( _ ByVal numberToTest As Integer, _ ByVal taskId As Object) ' Create an AsyncOperation for taskId. Dim asyncOp As AsyncOperation = _ AsyncOperationManager.CreateOperation(taskId) ' Multiple threads will access the task dictionary, ' so it must be locked to serialize access. SyncLock userStateToLifetime.SyncRoot If userStateToLifetime.Contains(taskId) Then Throw New ArgumentException( _ "Task ID parameter must be unique", _ "taskId") End If userStateToLifetime(taskId) = asyncOp End SyncLock ' Start the asynchronous operation. Dim workerDelegate As New WorkerEventHandler( _ AddressOf CalculateWorker) workerDelegate.BeginInvoke( _ numberToTest, _ asyncOp, _ Nothing, _ Nothing) End Sub
CancelAsync
메서드를 구현합니다.Implement theCancelAsync
method.taskId
매개 변수가 토큰 컬렉션에 있는 경우 제거됩니다.If thetaskId
parameter exists in the token collection, it is removed. 이렇게 하면 취소된 작업의 실행이 시작되지 않습니다.This prevents canceled tasks that have not started from running. 작업이 실행 중인 경우BuildPrimeNumberList
메서드는 해당 작업 ID가 수명 컬렉션에서 제거되었음을 감지할 때 종료됩니다.If the task is running, theBuildPrimeNumberList
method exits when it detects that the task ID has been removed from the lifetime collection.// This method cancels a pending asynchronous operation. public void CancelAsync(object taskId) { AsyncOperation asyncOp = userStateToLifetime[taskId] as AsyncOperation; if (asyncOp != null) { lock (userStateToLifetime.SyncRoot) { userStateToLifetime.Remove(taskId); } } }
' This method cancels a pending asynchronous operation. Public Sub CancelAsync(ByVal taskId As Object) Dim obj As Object = userStateToLifetime(taskId) If (obj IsNot Nothing) Then SyncLock userStateToLifetime.SyncRoot userStateToLifetime.Remove(taskId) End SyncLock End If End Sub
검사점Checkpoint
이때 구성 요소를 빌드할 수 있습니다.At this point, you can build the component.
구성 요소를 테스트하려면To test your component
- 구성 요소를 컴파일합니다.Compile the component.
이제 PrimeNumberCalculator
구성 요소가 완료되고 사용할 준비가 되었습니다.The PrimeNumberCalculator
component is now complete and ready to use.
PrimeNumberCalculator
구성 요소를 사용하는 예제 클라이언트는 방법: 이벤트 기반 비동기 패턴의 클라이언트 구현을 참조하세요.For an example client that uses the PrimeNumberCalculator
component, see How to: Implement a Client of the Event-based Asynchronous Pattern.
다음 단계Next Steps
CalculatePrimeAsync
메서드의 동기 메서드인 CalculatePrime
을 작성하여 이 예제를 채울 수 있습니다.You can fill out this example by writing CalculatePrime
, the synchronous equivalent of CalculatePrimeAsync
method. 이렇게 하면 PrimeNumberCalculator
구성 요소가 이벤트 기반 비동기 패턴을 완전히 준수하게 됩니다.This will make the PrimeNumberCalculator
component fully compliant with the Event-based Asynchronous Pattern.
여러 테스트 숫자에 대한 다양한 호출을 통해 검색된 모든 소수 목록을 유지하면 이 예제를 향상할 수 있습니다.You can improve this example by retaining the list of all the prime numbers discovered by various invocations for different test numbers. 이 방법을 사용하면 각 작업이 이전 작업에서 수행된 작업을 활용합니다.Using this approach, each task will benefit from the work done by previous tasks. 이 목록을 lock
영역으로 보호하면 여러 스레드에 의한 목록 액세스가 직렬화되므로 주의해야 합니다.Be careful to protect this list with lock
regions, so access to the list by different threads is serialized.
2, 3, 5와 같은 사소한 제수를 테스트하여 이 예제를 향상할 수도 있습니다.You can also improve this example by testing for trivial divisors, like 2, 3, and 5.