.NET에서 작업 기반 비동기 패턴은 새 개발에 권장되는 비동기 디자인 패턴입니다. 이는 비동기 작업을 나타내는 데 사용되는 Task 네임스페이스의 Task<TResult> 및 System.Threading.Tasks 형식에 기반합니다.
명명, 매개 변수 및 반환 형식
TAP은 단일 메서드를 사용하여 비동기 작업의 시작과 완료를 나타냅니다. 이는 APM IAsyncResult
(비동기 프로그래밍 모델) 패턴 및 EAP(이벤트 기반 비동기 패턴)와 대조됩니다. APM에는 Begin
및 End
메서드가 필요합니다. EAP에는 접미사 Async
가 있는 메서드와 하나 이상의 이벤트, 이벤트 처리기 대리자 유형 및 EventArg
로부터 파생된 유형이 필요합니다. TAP의 비동기 메서드에는 대기 가능한 형식(예: Async
, Task, Task<TResult>, ValueTask)을 반환하는 메서드의 작업 이름 뒤에 ValueTask<TResult> 접미사가 포함됩니다. 예를 들어, Get
를 반환하는 비동기 Task<String>
연산은 GetAsync
로 이름을 지정할 수 있습니다. 접미사가 있는 EAP 메서드 이름이 Async
이미 포함된 클래스에 TAP 메서드를 추가하는 경우 접미사를 TaskAsync
대신 사용합니다. 예를 들어 클래스에 이미 메서드가 있는 GetAsync
경우 이름을 GetTaskAsync
사용합니다. 메서드가 비동기 작업을 시작하지만 대기 가능한 형식을 반환하지 않는 경우, 메서드 이름은 이 작업의 결과를 반환하거나 throw하지 않음을 나타내기 위해 Begin
, Start
, 또는 다른 동사로 시작해야 합니다.
TAP 메서드는 해당 동기 메서드가 void를 반환하는지 아니면 형식System.Threading.Tasks.Task을 반환하는지에 따라 System.Threading.Tasks.Task<TResult> 또는 TResult
을 반환합니다.
TAP 메서드의 매개 변수는 동기 메서드의 매개 변수와 일치해야 하며 동일한 순서로 제공되어야 합니다. 그러나 out
ref
매개 변수는 이 규칙에서 제외되며 완전히 피해야 합니다.
out
또는 ref
매개 변수를 통해 반환되었을 모든 데이터는 TResult
에 의해 반환된 Task<TResult>의 일부로 반환되어야 하며, 여러 값을 수용하기 위해 튜플 또는 사용자 정의 데이터 구조를 사용해야 합니다. 또한 TAP 메서드의 동기식 버전이 매개 변수를 제공하지 않는 경우에도 CancellationToken 매개 변수를 추가하는 것이 좋습니다.
작업의 생성, 조작 또는 조합에만 전념하는 메서드(메서드의 비동기 의도가 메서드 이름 또는 메서드가 속한 형식의 이름에서 명확함)는 이 명명 패턴을 따를 필요가 없습니다. 이러한 메서드를 결합자라고도 합니다. 결합자의 예로는 WhenAll 및 WhenAny 등이 있으며, 이는 작업 기반 비동기 패턴 사용 문서의 기본 제공 작업 기반 결합기 사용 섹션에서 논의됩니다.
TAP 구문이 APM(비동기 프로그래밍 모델) 및 EAP(이벤트 기반 비동기 패턴)와 같은 레거시 비동기 프로그래밍 패턴에 사용되는 구문과 어떻게 다른지 예제는 비동기 프로그래밍 패턴을 참조하세요.
비동기 작업 시작
TAP을 기반으로 하는 비동기 메서드는 결과 작업을 반환하기 전에 인수 유효성 검사 및 비동기 작업 시작과 같은 적은 양의 작업을 동기적으로 수행할 수 있습니다. 비동기 메서드가 신속하게 반환될 수 있도록 동기 작업을 최소값으로 유지해야 합니다. 빠른 반환의 이유는 다음과 같습니다.
UI(사용자 인터페이스) 스레드에서 비동기 메서드를 호출할 수 있으며 장기 실행 동기 작업은 애플리케이션의 응답성을 손상시킬 수 있습니다.
여러 비동기 메서드를 동시에 시작할 수 있습니다. 따라서 비동기 메서드의 동기 부분에서 장기 실행 작업은 다른 비동기 작업의 시작을 지연시켜 동시성의 이점을 감소시킬 수 있습니다.
경우에 따라 작업을 완료하는 데 필요한 작업량이 비동기적으로 작업을 시작하는 데 필요한 작업량보다 적습니다. 이미 메모리에 버퍼링된 데이터로 읽기 작업을 충족할 수 있는 스트림에서 읽는 것이 이러한 시나리오의 예입니다. 이러한 경우 작업이 동기적으로 완료될 수 있으며 이미 완료된 작업을 반환할 수 있습니다.
예외
비동기 메서드는 오직 사용 오류에 대한 응답으로만 비동기 메서드 호출에서 throw되는 예외를 발생시켜야 합니다. 프로덕션 코드에서는 사용 오류가 발생하지 않아야 합니다. 예를 들어 메서드의 인수 중 하나로 null 참조(Nothing
Visual Basic)를 전달하면 오류 상태(일반적으로 예외로 ArgumentNullException 표시됨)가 발생하는 경우 null 참조가 전달되지 않도록 호출 코드를 수정할 수 있습니다. 다른 모든 오류의 경우 비동기 메서드가 실행 중일 때 발생하는 예외는 작업이 반환되기 전에 비동기 메서드가 동기적으로 완료되더라도 반환된 작업에 할당되어야 합니다. 일반적으로 태스크에는 최대 하나의 예외가 포함됩니다. 그러나 태스크가 여러 작업(예 WhenAll: )을 나타내는 경우 여러 예외가 단일 작업과 연결될 수 있습니다.
대상 환경
TAP 메서드를 구현할 때 비동기 실행이 발생하는 위치를 확인할 수 있습니다. 스레드 풀에서 워크로드를 실행하거나, 비동기 I/O를 사용하여 구현하거나(대부분의 작업 실행에서 스레드에 바인딩되지 않고), 특정 스레드(예: UI 스레드)에서 실행하거나, 가능한 컨텍스트 수를 사용할 수 있습니다. TAP 메서드는 실행할 항목이 없을 수도 있고 시스템의 다른 위치에서 발생한 조건(예: 대기 중인 데이터 구조에 도착하는 데이터를 나타내는 작업)을 반환 Task 할 수도 있습니다.
TAP 메서드의 호출자는 결과 작업을 동기적으로 대기하여 TAP 메서드가 완료될 때까지 기다리는 것을 차단하거나 비동기 작업이 완료되면 추가(연속) 코드를 실행할 수 있습니다. 연속 코드 작성자는 해당 코드가 실행되는 위치를 제어할 수 있습니다. 명시적으로는 Task 클래스의 메서드(예: ContinueWith)를 통해, 암시적으로는 연속 코드에 기반하여 구축된 언어 지원을 사용하여(예: C#의 await
, Visual Basic의 Await
, F#의 AwaitValue
) 연속 코드를 만들 수 있습니다.
작업 상태
클래스는 Task 비동기 작업에 대한 수명 주기를 제공하며 해당 주기는 열거형으로 TaskStatus 표시됩니다. Task 및 Task<TResult>에서 파생된 형식의 특수한 경우를 지원하고, 생성과 스케줄링의 분리를 지원하기 위해, Task 클래스는 Start 메서드를 제공합니다. 공용 Task 생성자가 만든 작업은 예약 되지 않은 상태에서 수명 주기를 시작하고 이러한 인스턴스에서 호출될 때만 Created 예약되기 때문에 Start이라고 합니다.
다른 모든 작업은 활성 상태에서 수명 주기를 시작합니다. 즉, 나타내는 비동기 작업이 이미 시작되었으며 해당 작업 상태는 열거형 값이 아닙니다 TaskStatus.Created. TAP 메서드에서 반환되는 모든 작업을 활성화해야 합니다. TAP 메서드가 내부적으로 태스크의 생성자를 사용하여 반환할 태스크를 인스턴스화하는 경우, TAP 메서드는 해당 태스크 개체를 반환하기 전에 Start을(를) Task 개체에서 호출해야 합니다. TAP 메서드의 사용자는 반환된 작업이 활성 상태임을 안전하게 가정할 수 있으며, TAP 메서드에서 반환된 작업에 대해 StartTask 호출을 시도하지 말아야 합니다. 활성 작업에서 Start를 호출하면 InvalidOperationException 예외가 발생합니다.
취소(선택 사항)
TAP에서 취소는 비동기 메서드 구현자와 비동기 메서드 소비자 모두에 대해 선택 사항입니다. 작업이 취소를 허용하는 경우 취소 토큰(CancellationToken 인스턴스)을 허용하는 비동기 메서드의 오버로드를 노출합니다. 규칙에 따라 매개 변수의 이름은 cancellationToken
.
public Task ReadAsync(byte [] buffer, int offset, int count,
CancellationToken cancellationToken)
Public Function ReadAsync(buffer() As Byte, offset As Integer,
count As Integer,
cancellationToken As CancellationToken) _
As Task
비동기 작업은 취소 요청에 대해 이 토큰을 모니터링합니다. 취소 요청을 받으면 해당 요청을 적용하고 작업을 취소하도록 선택할 수 있습니다. 취소 요청으로 인해 작업이 조기에 종료될 경우, TAP 메서드는 Canceled 상태에서 끝나는 작업을 반환합니다. 사용 가능한 결과가 없으며 예외가 발생하지 않습니다.
Canceled 상태는 Faulted 및 RanToCompletion 상태와 함께 작업에 대한 최종(완료된) 상태로 간주됩니다. 따라서 태스크가 Canceled 상태에 있으면 해당 IsCompleted 속성이 true
를 반환합니다. 작업이 Canceled 상태에서 완료되면, NotOnCanceled과 같은 연속 옵션을 지정하여 연속 실행을 옵트아웃하지 않는 한, 태스크에 등록된 모든 연속 작업이 예약되거나 실행됩니다. 언어 기능을 사용하여 취소된 작업을 비동기적으로 기다리는 코드는 계속 실행되지만, 그로 인해 발생한 OperationCanceledException 또는 파생된 예외를 받습니다.
Wait 및 WaitAll와 같은 메서드를 통해 작업을 동기적으로 대기하고 있는 코드도 예외가 발생하더라도 계속 실행됩니다.
취소 토큰이 해당 토큰을 수락하는 TAP 메서드 이전에 취소를 요청한 경우 TAP 메서드는 작업을 반환 Canceled 해야 합니다. 그러나 비동기 작업이 실행되는 동안 취소가 요청되는 경우 비동기 작업은 취소 요청을 수락할 필요가 없습니다. 작업이 취소 요청의 결과로 종료되는 경우에만 반환된 작업은 Canceled 상태로 종료됩니다. 취소가 요청되었지만 결과 또는 예외가 여전히 생성되는 경우 작업은 RanToCompletion 또는 Faulted 상태로 끝나야 합니다.
가장 먼저 취소할 수 있는 기능을 노출하려는 비동기 메서드의 경우 취소 토큰을 수락하지 않는 오버로드를 제공할 필요가 없습니다. 취소할 수 없는 메서드의 경우 취소 토큰을 수락하는 오버로드를 제공하지 않습니다. 이렇게 하면 대상 메서드를 실제로 취소할 수 있는지 여부를 호출자에게 알릴 수 있습니다. 취소를 원하지 않는 소비자 코드는 CancellationToken을 인수 값으로 제공하여 None 메서드를 호출할 수 있습니다. None 는 기능적으로 기본값 CancellationToken과 동일합니다.
진행률 보고(선택 사항)
일부 비동기 작업은 진행률 알림을 제공하는 이점을 제공합니다. 일반적으로 비동기 작업의 진행률에 대한 정보를 사용하여 사용자 인터페이스를 업데이트하는 데 사용됩니다.
TAP에서는 IProgress<T> 인터페이스를 통해 진행 상황이 처리되며, 이 인터페이스는 일반적으로 progress
라는 이름의 매개 변수로 비동기 메서드에 전달됩니다. 비동기 메서드가 호출될 때 진행률 인터페이스를 제공하면 잘못된 사용으로 인해 발생하는 경합 상태를 제거하는 데 도움이 됩니다(즉, 작업이 시작된 후 잘못 등록된 이벤트 처리기가 업데이트를 놓칠 수 있는 경우). 더 중요한 것은 진행률 인터페이스는 사용 코드에 의해 결정되는 다양한 진행률 구현을 지원합니다. 예를 들어 소비 코드는 최신 진행률 업데이트에만 관심이 있거나 모든 업데이트를 버퍼링하거나 각 업데이트에 대한 작업을 호출하거나 호출이 특정 스레드로 마샬링되는지 여부를 제어하려고 할 수 있습니다. 이러한 모든 옵션은 특정 소비자의 요구에 맞게 사용자 지정된 인터페이스의 다른 구현을 사용하여 수행할 수 있습니다. 취소와 마찬가지로 TAP 구현은 API가 IProgress<T> 진행률 알림을 지원하는 경우에만 매개 변수를 제공해야 합니다.
예를 들어 이 문서의 앞부분에서 설명한 메서드가 지금까지 읽은 바이트 수의 형태로 중간 진행률을 보고할 수 있는 경우 ReadAsync
진행률 콜백은 인터페이스일 IProgress<T> 수 있습니다.
public Task ReadAsync(byte[] buffer, int offset, int count,
IProgress<long> progress)
Public Function ReadAsync(buffer() As Byte, offset As Integer,
count As Integer,
progress As IProgress(Of Long)) As Task
메서드가 FindFilesAsync
특정 검색 패턴을 충족하는 모든 파일 목록을 반환하는 경우 진행률 콜백은 완료된 작업의 백분율과 현재 부분 결과 집합의 예상값을 제공할 수 있습니다. 다음 중 하나의 튜플을 사용하여 이 정보를 제공할 수 있습니다.
public Task<ReadOnlyCollection<FileInfo>> FindFilesAsync(
string pattern,
IProgress<Tuple<double,
ReadOnlyCollection<List<FileInfo>>>> progress)
Public Function FindFilesAsync(pattern As String,
progress As IProgress(Of Tuple(Of Double, ReadOnlyCollection(Of List(Of FileInfo))))) _
As Task(Of ReadOnlyCollection(Of FileInfo))
또는 API와 관련된 데이터 형식을 사용합니다.
public Task<ReadOnlyCollection<FileInfo>> FindFilesAsync(
string pattern,
IProgress<FindFilesProgressInfo> progress)
Public Function FindFilesAsync(pattern As String,
progress As IProgress(Of FindFilesProgressInfo)) _
As Task(Of ReadOnlyCollection(Of FileInfo))
후자의 경우 특수 데이터 형식은 일반적으로 접미사가 붙습니다 ProgressInfo
.
TAP 구현에서 매개 변수를 허용하는 progress
오버로드를 제공하는 경우 인수 null
를 허용해야 합니다. 이 경우 진행률이 보고되지 않습니다. TAP 구현은 개체에 Progress<T> 진행률을 동기적으로 보고해야 하므로 비동기 메서드가 신속하게 진행률을 제공할 수 있습니다. 또한 정보를 가장 잘 처리할 방법과 위치를 결정할 수 있는 것은 이러한 진행률 소비자에게 달려 있습니다. 예를 들어, 진행 상황 인스턴스는 콜백을 마샬링하고 캡처된 동기화 컨텍스트에서 이벤트를 발생시키도록 선택할 수 있습니다.
IProgress<T> 인터페이스 구현
.NET은 Progress<T> 클래스를 제공하여 IProgress<T>을(를) 구현합니다. Progress<T> 클래스는 다음과 같이 선언됩니다.
public class Progress<T> : IProgress<T>
{
public Progress();
public Progress(Action<T> handler);
protected virtual void OnReport(T value);
public event EventHandler<T>? ProgressChanged;
}
Progress<T>의 인스턴스는 비동기 작업이 진행률 업데이트를 보고할 때마다 발생하는 ProgressChanged 이벤트를 노출합니다. ProgressChanged 인스턴스가 인스턴스화될 때 캡처된 SynchronizationContext 개체에서 Progress<T> 이벤트가 발생합니다. 동기화 컨텍스트를 사용할 수 없는 경우 스레드 풀을 대상으로 하는 기본 컨텍스트가 사용됩니다. 핸들러는 이 이벤트에 등록될 수 있습니다. 또한 편의를 위해 Progress<T> 생성자에 단일 처리기를 제공할 수 있으며 이벤트에 대한 ProgressChanged 이벤트 처리기처럼 동작합니다. 이벤트 처리기가 실행되는 동안 비동기 작업이 지연되지 않도록 진행률 업데이트가 비동기적으로 발생합니다. 다른 IProgress<T> 구현은 다른 의미 체계를 적용하도록 선택할 수 있습니다.
제공할 오버로드 선택
TAP 구현에서 선택적 매개 변수와 선택적 CancellationTokenIProgress<T> 매개 변수를 모두 사용하는 경우 최대 4개의 오버로드가 필요할 수 있습니다.
public Task MethodNameAsync(…);
public Task MethodNameAsync(…, CancellationToken cancellationToken);
public Task MethodNameAsync(…, IProgress<T> progress);
public Task MethodNameAsync(…,
CancellationToken cancellationToken, IProgress<T> progress);
Public MethodNameAsync(…) As Task
Public MethodNameAsync(…, cancellationToken As CancellationToken cancellationToken) As Task
Public MethodNameAsync(…, progress As IProgress(Of T)) As Task
Public MethodNameAsync(…, cancellationToken As CancellationToken,
progress As IProgress(Of T)) As Task
그러나 많은 TAP 구현은 취소 또는 진행률 기능을 제공하지 않으므로 단일 메서드가 필요합니다.
public Task MethodNameAsync(…);
Public MethodNameAsync(…) As Task
TAP 구현에서 취소나 진행률 둘 중 하나만 지원하고, 둘 다 지원하지 않는 경우 두 가지 오버로드를 제공할 수 있습니다.
public Task MethodNameAsync(…);
public Task MethodNameAsync(…, CancellationToken cancellationToken);
// … or …
public Task MethodNameAsync(…);
public Task MethodNameAsync(…, IProgress<T> progress);
Public MethodNameAsync(…) As Task
Public MethodNameAsync(…, cancellationToken As CancellationToken) As Task
' … or …
Public MethodNameAsync(…) As Task
Public MethodNameAsync(…, progress As IProgress(Of T)) As Task
TAP 구현에서 취소 및 진행률을 모두 지원하는 경우 4개의 오버로드를 모두 노출할 수 있습니다. 그러나 다음 두 가지만 제공할 수 있습니다.
public Task MethodNameAsync(…);
public Task MethodNameAsync(…,
CancellationToken cancellationToken, IProgress<T> progress);
Public MethodNameAsync(…) As Task
Public MethodNameAsync(…, cancellationToken As CancellationToken,
progress As IProgress(Of T)) As Task
누락된 두 중간 조합을 보상하기 위해 개발자는 None 매개 변수에 대해 CancellationToken 또는 기본값 cancellationToken
을 전달하고, null
매개 변수에 대해 progress
을 사용할 수 있습니다.
TAP 메서드의 모든 사용이 취소 또는 진행률을 지원할 것으로 예상되는 경우 관련 매개 변수를 허용하지 않는 오버로드를 생략할 수 있습니다.
취소 또는 진행률을 선택적으로 만들기 위해 여러 오버로드를 노출하기로 결정한 경우, 취소 또는 진행을 지원하지 않는 오버로드는 취소를 위해 None 또는 진행을 위해 null
을(를) 취소나 진행을 지원하는 오버로드에 전달된 것처럼 동작해야 합니다.
관련 문서
제목 | 설명 |
---|---|
비동기 프로그래밍 패턴 | 비동기 작업을 수행하기 위한 세 가지 패턴인 TAP(작업 기반 비동기 패턴), APM(비동기 프로그래밍 모델) 및 이벤트 기반 EAP(비동기 패턴)를 소개합니다. |
작업 기반 비동기 패턴 구현 | Visual Studio에서 C# 및 Visual Basic 컴파일러를 수동으로 사용하거나 컴파일러와 수동 메서드의 조합을 통해 세 가지 방법으로 TAP(작업 기반 비동기 패턴)을 구현하는 방법을 설명합니다. |
작업 기반 비동기 패턴 사용 | 작업 및 콜백을 사용하여 차단 없이 대기하는 방법을 설명합니다. |
다른 비동기 패턴 및 유형과의 상호 운용성 | TAP(작업 기반 비동기 패턴)를 사용하여 APM(비동기 프로그래밍 모델) 및 EAP(이벤트 기반 비동기 패턴)를 구현하는 방법을 설명합니다. |
.NET