최선의 이벤트 기반 비동기 패턴 구현 방법
이벤트 기반 비동기 패턴에서는 익숙한 이벤트와 대리자 의미를 사용하여 클래스에서 비동기 동작을 노출할 수 있는 효과적인 방법을 제공합니다. 이벤트 기반 비동기 패턴을 구현하려면 몇 가지 특정 동작 요구 사항을 준수해야 합니다. 다음 단원에서는 이벤트 기반 비동기 패턴을 따르는 클래스를 구현할 때 고려해야 할 요구 사항과 지침에 대해 설명합니다.
전체적인 개요를 보려면 이벤트 기반 비동기 패턴 구현를 참조하십시오.
다음 목록에는 이 항목에서 설명하는 최선의 방법이 표시됩니다.
필요한 동작 보장
완료
완료된 이벤트 및 EventArgs
작업 동시 실행
결과 액세스
진행률 보고
IsBusy 구현
취소
오류 및 예외
스레딩 및 컨텍스트
지침
필요한 동작 보장
이벤트 기반 비동기 패턴을 구현할 경우 클래스가 제대로 동작하고 클래스의 클라이언트가 이러한 동작을 신뢰할 수 있도록 충분히 보장해야 합니다.
완료
성공적으로 완료하거나 오류가 발생하거나 취소한 경우 MethodNameCompleted 이벤트 처리기를 항상 호출합니다. 응용 프로그램이 유휴 상태로 남아 완료될 수 없는 상황이 발생하면 안 됩니다. 비동기 동작 자체가 완료되지 않도록 설계된 경우에만 이 규칙이 적용되지 않습니다.
완료된 이벤트 및 EventArgs
별개의 MethodNameAsync 메서드에 대해 다음 디자인 요구 사항이 적용됩니다.
MethodNameCompleted 이벤트를 메서드와 같은 클래스에서 정의합니다.
AsyncCompletedEventArgs 클래스에서 파생되는 MethodNameCompleted 이벤트의 EventArgs 클래스와 관련 대리자를 정의합니다. 기본 클래스 이름은 MethodNameCompletedEventArgs 형식이어야 합니다.
EventArgs 클래스가 MethodName 메서드의 반환 값으로만 한정되도록 합니다. EventArgs 클래스를 사용할 때 개발자가 결과를 캐스팅하지 않도록 해야 합니다.
다음 코드 예제에서는 이 디자인 요구 사항을 제대로 구현한 것과 잘못 구현한 것을 각각 보여 줍니다.
[C#]
// Good design
private void Form1_MethodNameCompleted(object sender, xxxCompletedEventArgs e)
{
DemoType result = e.Result;
}
// Bad design
private void Form1_MethodNameCompleted(object sender, MethodNameCompletedEventArgs e)
{
DemoType result = (DemoType)(e.Result);
}
void를 반환하는 메서드를 반환하도록 EventArgs 클래스를 정의하지 마십시오. 대신 AsyncCompletedEventArgs 클래스의 인스턴스를 사용하십시오.
항상 MethodNameCompleted 이벤트가 발생하는지 확인하십시오. 이 이벤트는 작업이 완료되었거나, 오류가 발생했거나, 취소된 경우에 발생해야 합니다. 응용 프로그램이 유휴 상태로 남아 완료될 수 없는 상황이 발생하면 안 됩니다.
비동기 작업에서 발생하는 예외를 catch하고 catch한 예외를 Error 속성에 할당합니다.
작업을 완료할 때 오류가 발생하면 결과에 액세스할 수 없습니다. Error 속성이 null이 아니면 EventArgs 구조에서 속성에 액세스할 때 예외가 발생하는지 확인합니다. 이러한 확인 작업을 수행하려면 RaiseExceptionIfNecessary 메서드를 사용합니다.
시간 초과를 오류로 모델링합니다. 시간 초과가 발생하면 MethodNameCompleted 이벤트를 일으키고 TimeoutException을 Error 속성에 할당합니다.
클래스에서 다중 동시 호출이 지원될 경우 MethodNameCompleted 이벤트에 적합한 userSuppliedState 개체가 포함되는지 확인합니다.
응용 프로그램 수명의 적절한 시기에 적절한 스레드에서 MethodNameCompleted 이벤트가 발생하도록 합니다. 자세한 내용은 스레딩 및 컨텍스트 단원을 참조하십시오.
작업 동시 실행
클래스가 여러 개의 동시 호출을 지원하는 경우 개발자가 userSuppliedState라는 개체 값 상태 매개 변수 또는 작업 ID를 가지는 MethodNameAsync 오버로드를 정의하여 각 호출을 개별적으로 추적할 수 있습니다. 이 매개 변수는 항상 MethodNameAsync 메서드의 시그니처에서 마지막 매개 변수여야 합니다.
클래스에서 개체 값 상태 매개 변수 또는 작업 ID를 가지는 MethodNameAsync 오버로드를 정의하는 경우 해당 작업 ID의 작업에 대한 수명을 추적하고 이를 완료 처리기에 다시 제공해야 합니다. 이 작업에 도움을 줄 수 있는 도우미 클래스가 있습니다. 동시성 관리에 대한 자세한 내용은 연습: 이벤트 기반 비동기 패턴을 지원하는 구성 요소 구현을 참조하십시오.
클래스에서 상태 매개 변수 없이 MethodNameAsync 메서드를 정의하고 여러 개의 동시 호출을 지원하지 않는 경우 이전 MethodNameAsync 호출이 완료되기 전에 MethodNameAsync를 호출하려고 하면 InvalidOperationException이 발생하도록 합니다.
일반적으로 userSuppliedState 매개 변수 없이 MethodNameAsync 메서드를 여러 번 호출해서 처리 중인 작업이 여럿 있도록 하는 경우 예외가 발생하지 않습니다. 클래스에서 이러한 상황을 명시적으로 처리할 수 없는 경우 예외를 발생시킬 수 있지만 개발자는 이러한 구분할 수 없는 여러 콜백을 처리할 수 있습니다.
결과 액세스
비동기 작업을 실행하는 동안 오류가 발생하면 그 결과에 액세스할 수 없습니다. Error가 null이 아닌 경우 AsyncCompletedEventArgs의 속성에 액세스하면 Error에서 참조하는 예외가 발생하도록 합니다. AsyncCompletedEventArgs 클래스는 이러한 목적으로 RaiseExceptionIfNecessary 메서드를 제공합니다.
결과에 액세스하려고 하면 작업이 취소되었음을 나타내는 InvalidOperationException이 발생하도록 합니다. 이러한 확인 작업을 수행하려면 AsyncCompletedEventArgs.RaiseExceptionIfNecessary 메서드를 사용합니다.
진행률 보고
가능하면 진행률 보고를 지원합니다. 이렇게 하면 개발자가 클래스 사용자에게 더 나은 응용 프로그램 경험을 제공할 수 있습니다.
ProgressChanged/MethodNameProgressChanged 이벤트를 구현할 경우 작업의 MethodNameCompleted 이벤트가 발생한 후 특정 비동기 작업에 대해 이러한 이벤트가 발생하지 않도록 합니다.
표준 ProgressChangedEventArgs를 채우는 경우 ProgressPercentage가 항상 백분율로 해석될 수 있도록 합니다. 백분율이 정확할 필요는 없지만 백분율을 나타내야 합니다. 진행률 보고 메트릭이 백분율이 아니어야 하는 경우 ProgressChangedEventArgs 클래스에서 클래스를 파생하고 ProgressPercentage를 0으로 둡니다. 백분율 이외의 보고 메트릭을 사용하지 않는 것이 좋습니다.
응용 프로그램 수명의 적절한 시기에 적절한 스레드에서 ProgressChanged 이벤트가 발생하도록 합니다. 자세한 내용은 스레딩 및 컨텍스트 단원을 참조하십시오.
IsBusy 구현
클래스에서 여러 개의 동시 호출을 지원하는 경우 IsBusy 속성을 노출하지 마십시오. 예를 들어, XML Web services 프록시는 비동기 메서드에 대해 여러 개의 동시 호출을 지원하기 때문에 IsBusy 속성을 노출하지 않습니다.
IsBusy 속성은 MethodNameAsync 메서드를 호출한 후 MethodNameCompleted 이벤트가 발생하기 전까지 true를 반환해야 합니다. 그렇지 않으면 false를 반환해야 합니다. BackgroundWorker 및 WebClient 구성 요소는 IsBusy 속성을 노출하는 클래스의 예입니다.
취소
가능하면 취소를 지원합니다. 이렇게 하면 개발자가 클래스 사용자에게 더 나은 응용 프로그램 경험을 제공할 수 있습니다.
취소의 경우 AsyncCompletedEventArgs 개체에서 Cancelled 플래그를 설정합니다.
결과에 액세스하려고 하면 작업이 취소되었음을 나타내는 InvalidOperationException이 발생하도록 합니다. 이러한 확인 작업을 수행하려면 AsyncCompletedEventArgs.RaiseExceptionIfNecessary 메서드를 사용합니다.
취소 메서드를 호출하면 항상 성공적으로 반환되고 예외를 발생시키지 않도록 합니다. 일반적으로 클라이언트는 지정된 시간에 작업을 실제로 취소할 수 있는지 여부와 이전에 실행한 취소가 성공했는지 여부에 대한 알림을 받지 않습니다. 그러나 응용 프로그램은 완료 상태에 참여하기 때문에 취소가 성공하면 항상 알림을 받습니다.
작업이 취소되면 MethodNameCompleted 이벤트를 발생시킵니다.
오류 및 예외
- 비동기 작업에서 발생하는 예외를 모두 catch하고 AsyncCompletedEventArgs.Error 속성 값을 해당 예외로 설정합니다.
스레딩 및 컨텍스트
클래스가 제대로 작동하기 위해서는 ASP.NET 및 Windows Forms 응용 프로그램을 포함하는 지정된 응용 프로그램 모델에 대해 적절한 스레드 또는 컨텍스트에서 클라이언트의 이벤트 처리기를 호출하는 것이 중요합니다. 비동기 클래스가 모든 응용 프로그램 모델에서 제대로 동작할 수 있도록 AsyncOperation 및 AsyncOperationManager라는 두 개의 중요한 도우미 클래스가 제공됩니다.
AsyncOperationManager에서 AsyncOperation을 반환하는 CreateOperation이라는 메서드 하나를 제공합니다. MethodNameAsync 메서드는 CreateOperation을 호출하고 클래스는 반환된 AsyncOperation을 사용하여 비동기 작업의 수명을 추적합니다.
진행률, 증분 결과 및 완료를 클라이언트에 보고하려면 AsyncOperation에서 Post 및 OperationCompleted 메서드를 호출합니다. AsyncOperation은 클라이언트의 이벤트 처리기에 대한 호출을 적절한 스레드나 컨텍스트로 마샬링합니다.
참고 |
---|
응용 프로그램 모델의 정책을 명시적으로 따르지 않으려는 경우 이러한 규칙을 우회할 수 있지만 이벤트 기반 비동기 패턴을 사용하는 데 따르는 다른 이점은 그대로 누릴 수 있습니다.예를 들어, Windows Forms에서 작동하는 클래스가 자유 스레드되도록 할 수 있습니다.개발자가 암시적 제한 사항을 알고 있는 경우 자유 스레드된 클래스를 만들 수 있습니다.콘솔 응용 프로그램에서는 Post 호출의 실행을 동기화하지 않습니다.이 경우 ProgressChanged 이벤트가 잘못된 순서로 실행됩니다.Post 호출의 실행이 serialize되도록 하려면 System.Threading.SynchronizationContext 클래스를 구현하고 설치합니다. |
AsyncOperation 및 AsyncOperationManager를 사용하여 비동기 작업을 사용할 수 있도록 설정하는 방법에 대한 자세한 내용은 연습: 이벤트 기반 비동기 패턴을 지원하는 구성 요소 구현을 참조하십시오.
지침
이상적으로 각 메서드 호출은 서로 독립적이어야 합니다. 공유 리소스를 포함하는 호출을 결합하지 마십시오. 호출 간에 리소스를 공유하려면 구현에서 적절한 동기화 메커니즘을 제공해야 합니다.
클라이언트가 동기화를 구현해야 하는 디자인은 권장하지 않습니다. 예를 들어, 전역 정적 개체를 매개 변수로 받는 비동기 메서드가 있을 수 있습니다. 이러한 메서드를 동시에 여러 번 호출하면 데이터 손상이나 교착 상태가 발생할 수 있습니다.
다중 호출 오버로드(시그니처의 userState)를 사용하여 메서드를 구현하는 경우 클래스에서 사용자 상태나 작업 ID 및 해당하는 보류 중 작업의 컬렉션을 관리해야 합니다. 여러 호출에서 컬렉션의 userState 개체를 추가 및 제거하므로 이 컬렉션을 lock 영역으로 보호해야 합니다.
가능하거나 적절한 경우 CompletedEventArgs 클래스를 다시 사용할 수 있습니다. 이 경우 지정된 대리자와 EventArgs 형식이 하나의 메서드와만 관련된 것이 아니므로 메서드 이름 지정이 일관되지 않습니다. 그러나 개발자가 EventArgs의 속성에서 검색한 값을 캐스팅하도록 하는 것은 좋지 않습니다.
Component에서 파생되는 클래스를 만드는 경우에는 사용자가 직접 SynchronizationContext 클래스를 구현하고 설치하지 마십시오. 구성 요소가 아니라 응용 프로그램 모델이 사용되는 SynchronizationContext를 제어합니다.
다중 스레딩을 사용할 경우 매우 심각하고 복잡한 버그에 노출될 위험이 있습니다. 다중 스레딩을 사용하는 솔루션을 구현하기 전에 관리되는 스레딩을 구현하는 최선의 방법을 참조하십시오.
참고 항목
작업
방법: 이벤트 기반 비동기 패턴을 지원하는 구성 요소 사용
연습: 이벤트 기반 비동기 패턴을 지원하는 구성 요소 구현