CancellationToken을 활용한 작업 취소 기법

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();
    }
}

태그: CancellationToken CancellationTokenSource ASP.NET Core

5월 23일 11:30에 게시됨