1. CancellationToken 생성하기
1.1 CancellationTokenSource를 통한 생성 (권장)
CancellationTokenSource는 취소 요청을 발생시키는 역할을 하며, 이에 대응하는 CancellationToken은 해당 요청에 반응하여 작업을 중단시킵니다.
var cancellationSource = new CancellationTokenSource();
var token = cancellationSource.Token;
1.2 직접 생성 또는 None 사용
이 방식으로 생성된 토큰은 초기 상태(취소 여부)가 고정되어 변경되지 않습니다. 특정 상황에서만 사용됩니다.
var token = new CancellationToken(false);
var noneToken = CancellationToken.None;
2. 다양한 취소 방법
2.1 타임아웃 기반 취소
특정 시간 내에 작업이 완료되지 않으면 자동으로 취소됩니다.
static async Task Main(string[] args)
{
var timeoutSource = new CancellationTokenSource(5000); // 5초 후 자동 취소
Console.WriteLine($"작업 시작: {DateTime.Now}");
await FetchDataAsync("https://example.com", 5, timeoutSource.Token);
}
static async Task FetchDataAsync(string url, int attempts, CancellationToken token)
{
using var client = new HttpClient();
for (int i = 0; i < attempts; i++)
{
try
{
var content = await client.GetStringAsync(url);
Console.WriteLine($"데이터 수신: {DateTime.Now}");
token.ThrowIfCancellationRequested(); // 취소 요청 감지 시 예외 발생
}
catch (OperationCanceledException)
{
Console.WriteLine("작업이 취소되었습니다.");
break;
}
}
}
2.2 API 내부 취소 처리
일부 비동기 메서드는 내부적으로 CancellationToken을 처리하여 보다 신속하게 작업을 중단합니다.
static async Task FetchDataWithApiSupport(string url, int attempts, CancellationToken token)
{
using var client = new HttpClient();
for (int i = 0; i < attempts; i++)
{
try
{
var response = await client.GetAsync(url, token);
if (!response.IsSuccessStatusCode) throw new HttpRequestException("요청 실패");
var content = await response.Content.ReadAsStringAsync();
Console.WriteLine($"성공: {DateTime.Now}");
}
catch (OperationCanceledException)
{
Console.WriteLine("API 호출 취소됨");
break;
}
}
}
2.3 수동 취소
사용자 입력이나 특정 조건에 따라 작업을 수동으로 취소할 수 있습니다.
static async Task Main(string[] args)
{
var source = new CancellationTokenSource();
_ = FetchDataAsync("https://example.com", 10, source.Token);
Console.WriteLine("취소하려면 'q'를 입력하세요:");
while (Console.ReadLine() != "q") { }
source.Cancel();
Console.WriteLine("작업 취소 명령 전달됨.");
}
2.4 취소 콜백 등록
취소가 발생했을 때 실행될 콜백을 미리 등록할 수 있습니다.
var source = new CancellationTokenSource();
source.Token.Register(() =>
{
Console.WriteLine("현재 작업이 취소되었습니다.");
});
Task.Run(async () =>
{
while (!source.IsCancellationRequested)
{
await Task.Delay(500, source.Token);
Console.WriteLine("작업 진행 중...");
}
});
await Task.Delay(2000);
source.Cancel();
2.5 다중 취소 조합
여러 CancellationToken을 결합하여 하나의 취소 신호로 관리할 수 있습니다.
var source1 = new CancellationTokenSource();
var source2 = new CancellationTokenSource();
var combinedSource = CancellationTokenSource.CreateLinkedTokenSource(source1.Token, source2.Token);
Task.Run(async () =>
{
while (!combinedSource.Token.IsCancellationRequested)
{
await Task.Delay(1000);
Console.WriteLine("작업 실행 중...");
}
});
await Task.Delay(3000);
source1.Cancel(); // source1 취소 시 전체 작업도 취소됨
3. ASP.NET Core MVC에서의 CancellationToken
컨트롤러 액션에서 CancellationToken을 파라미터로 받을 수 있어 클라이언트 요청 취소와 같은 시나리오에 유용합니다.
public async Task<IActionResult> LoadData(CancellationToken cancellationToken)
{
try
{
await ProcessDataAsync("https://example.com", 10, cancellationToken);
return View();
}
catch (OperationCanceledException)
{
Console.WriteLine("클라이언트 요청 취소됨.");
return StatusCode(StatusCodes.Status499ClientClosedRequest);
}
}
static async Task ProcessDataAsync(string url, int iterations, CancellationToken token)
{
using var client = new HttpClient();
for (int i = 0; i < iterations; i++)
{
var response = await client.GetAsync(url, token);
var data = await response.Content.ReadAsStringAsync();
Console.WriteLine($"처리 완료: {i + 1}");
token.ThrowIfCancellationRequested();
}
}