C#의 CancellationToken, CancellationTokenSource

🔥 CancellationToken, CancellationTokenSource

https://learn.microsoft.com/ko-kr/dotnet/api/system.threading.cancellationtokensource?view=net-9.0

✅ 개념 정리

CancellationTokenCancellationTokenSource는 C#에서 비동기 작업(Task)이나 스레드를 안전하게 취소할 수 있도록 제공되는 기능합니다.

이를 활용하면 비동기 작업을 중단하거나, 긴 루프를 중지할 수 있도록 관리할 수 있습니다.

🔹 CancellationTokenSource

  • 취소 요청을 생성하고 관리하는 역할을 합니다.
  • CancellationToken을 생성할 수 있으며, Cancel() 메서드를 호출하면 해당 토큰을 통해 작업을 중단할 수 있습니다.

🔹 CancellationToken

  • CancellationTokenSource에서 생성한 취소 토큰을 비동기 작업이나 스레드에 전달하여, 취소 여부를 확인하는 데 사용됩니다.
역할설명
CancellationTokenSource취소 요청을 보내는 쪽 (발신자) 역할을 합니다. Cancel(), CancelAfter(), Dispose() 등을 호출 할 수 있습니다.
CancellationToken취소 요청을 받는 쪽 (수신자) 역할입니다. IsCancellationRequested로 확인하거나, ThrowIfCancellationRequested() 등을 사용해 취소 여부를 감지할 수 있습니다.

✅ 기본적인 사용법

🔹 CancellationToken 사용 예제

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
using System;
using System.Threading;
using System.Threading.Tasks;
class Program
{
static async Task Main()
{
CancellationTokenSource cts = new CancellationTokenSource();
CancellationToken token = cts.Token;
Task task = DoWorkAsync(token);
// 3초 후에 작업 취소 요청
await Task.Delay(3000);
cts.Cancel();
await task;
}
static async Task DoWorkAsync(CancellationToken token)
{
for (int i = 0; i < 10; i++)
{
// 작업이 취소되었는지 확인
if (token.IsCancellationRequested)
{
Console.WriteLine("작업이 취소되었습니다.");
return;
}
Console.WriteLine($"작업 진행 중... {i}");
await Task.Delay(1000); // 1초 대기
}
}
}
using System; using System.Threading; using System.Threading.Tasks; class Program { static async Task Main() { CancellationTokenSource cts = new CancellationTokenSource(); CancellationToken token = cts.Token; Task task = DoWorkAsync(token); // 3초 후에 작업 취소 요청 await Task.Delay(3000); cts.Cancel(); await task; } static async Task DoWorkAsync(CancellationToken token) { for (int i = 0; i < 10; i++) { // 작업이 취소되었는지 확인 if (token.IsCancellationRequested) { Console.WriteLine("작업이 취소되었습니다."); return; } Console.WriteLine($"작업 진행 중... {i}"); await Task.Delay(1000); // 1초 대기 } } }
using System;
using System.Threading;
using System.Threading.Tasks;

class Program
{
    static async Task Main()
    {
        CancellationTokenSource cts = new CancellationTokenSource();
        CancellationToken token = cts.Token;

        Task task = DoWorkAsync(token);

        // 3초 후에 작업 취소 요청
        await Task.Delay(3000);
        cts.Cancel();

        await task;
    }

    static async Task DoWorkAsync(CancellationToken token)
    {
        for (int i = 0; i < 10; i++)
        {
            // 작업이 취소되었는지 확인
            if (token.IsCancellationRequested)
            {
                Console.WriteLine("작업이 취소되었습니다.");
                return;
            }

            Console.WriteLine($"작업 진행 중... {i}");
            await Task.Delay(1000); // 1초 대기
        }
    }
}
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
작업 진행 중... 0
작업 진행 중... 1
작업 진행 중... 2
작업이 취소되었습니다.
작업 진행 중... 0 작업 진행 중... 1 작업 진행 중... 2 작업이 취소되었습니다.
작업 진행 중... 0
작업 진행 중... 1
작업 진행 중... 2
작업이 취소되었습니다.
🔍 설명
  1. CancellationTokenSource를 생성하여 CancellationToken을 가져옴
  2. DoWorkAsync() 메서드에서 token.IsCancellationRequested를 통해 취소 여부 확인
  3. Task.Delay(3000)cts.Cancel()을 호출하여 비동기 작업을 취소

✅ 알아두면 유용한 기능

🔹 ThrowIfCancellationRequested()

이전의 예제에서는 IsCancellationRequested로 확인 후 return 했지만, ThrowIfCancellationRequested()를 사용하면 예외(Exception) 형태로 중단할 수도 있습니다.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
static async Task DoWorkAsync(CancellationToken token)
{
for (int i = 0; i < 10; i++)
{
token.ThrowIfCancellationRequested(); // 취소 요청 시 예외 발생
Console.WriteLine($"작업 진행 중... {i}");
await Task.Delay(1000);
}
}
static async Task DoWorkAsync(CancellationToken token) { for (int i = 0; i < 10; i++) { token.ThrowIfCancellationRequested(); // 취소 요청 시 예외 발생 Console.WriteLine($"작업 진행 중... {i}"); await Task.Delay(1000); } }
static async Task DoWorkAsync(CancellationToken token)
{
    for (int i = 0; i < 10; i++)
    {
        token.ThrowIfCancellationRequested(); // 취소 요청 시 예외 발생

        Console.WriteLine($"작업 진행 중... {i}");
        await Task.Delay(1000);
    }
}

예외 처리 추가 (try-catch로 OperationCanceledException을 catch가 가능)

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
try
{
await DoWorkAsync(token);
}
catch (OperationCanceledException)
{
Console.WriteLine("작업이 취소되었습니다. (예외 발생)");
}
try { await DoWorkAsync(token); } catch (OperationCanceledException) { Console.WriteLine("작업이 취소되었습니다. (예외 발생)"); }
try
{
    await DoWorkAsync(token);
}
catch (OperationCanceledException)
{
    Console.WriteLine("작업이 취소되었습니다. (예외 발생)");
}

🔹 Task.Run()에서 CancellationToken

비동기 작업을 Task.Run()을 사용하여 실행하는 경우, CancellationToken을 직접 전달할 수 있습니다.

Task.Run()과 함께 사용할 경우, CancellationToken을 직접 전달하면 자동으로 OperationCanceledException이 발생하여 처리하기 편리합니다.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
CancellationTokenSource cts = new CancellationTokenSource();
Task task = Task.Run(() =>
{
for (int i = 0; i < 10; i++)
{
cts.Token.ThrowIfCancellationRequested();
Console.WriteLine($"작업 진행 중... {i}");
Thread.Sleep(1000);
}
}, cts.Token); // CancellationToken을 직접 전달
await Task.Delay(3000);
cts.Cancel(); // 3초 후 취소
CancellationTokenSource cts = new CancellationTokenSource(); Task task = Task.Run(() => { for (int i = 0; i < 10; i++) { cts.Token.ThrowIfCancellationRequested(); Console.WriteLine($"작업 진행 중... {i}"); Thread.Sleep(1000); } }, cts.Token); // CancellationToken을 직접 전달 await Task.Delay(3000); cts.Cancel(); // 3초 후 취소
CancellationTokenSource cts = new CancellationTokenSource();

Task task = Task.Run(() => 
{
    for (int i = 0; i < 10; i++)
    {
        cts.Token.ThrowIfCancellationRequested();
        Console.WriteLine($"작업 진행 중... {i}");
        Thread.Sleep(1000);
    }
}, cts.Token); // CancellationToken을 직접 전달

await Task.Delay(3000);
cts.Cancel(); // 3초 후 취소

🔹 Register()를 활용한 CallBack 함수

CancellationToken.Register()를 사용하면, 취소될 때 특정 작업을 실행할 수 있습니다.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
cts.Token.Register(() => Console.WriteLine("취소 요청이 감지되었습니다!"));
cts.Token.Register(() => Console.WriteLine("취소 요청이 감지되었습니다!"));
cts.Token.Register(() => Console.WriteLine("취소 요청이 감지되었습니다!"));

사용 예제

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
using System;
using System.Threading;
using System.Threading.Tasks;
class Program
{
static async Task Main()
{
CancellationTokenSource cts = new CancellationTokenSource();
CancellationToken token = cts.Token;
// 취소 요청 시 실행될 콜백 등록
token.Register(() => Console.WriteLine("취소 요청이 감지되었습니다!"));
Task task = DoWorkAsync(token);
await Task.Delay(3000);
cts.Cancel(); // 3초 후 취소 요청
await task;
}
static async Task DoWorkAsync(CancellationToken token)
{
for (int i = 0; i < 10; i++)
{
if (token.IsCancellationRequested)
{
Console.WriteLine("작업이 취소되었습니다.");
return;
}
Console.WriteLine($"작업 진행 중... {i}");
await Task.Delay(1000);
}
}
}
using System; using System.Threading; using System.Threading.Tasks; class Program { static async Task Main() { CancellationTokenSource cts = new CancellationTokenSource(); CancellationToken token = cts.Token; // 취소 요청 시 실행될 콜백 등록 token.Register(() => Console.WriteLine("취소 요청이 감지되었습니다!")); Task task = DoWorkAsync(token); await Task.Delay(3000); cts.Cancel(); // 3초 후 취소 요청 await task; } static async Task DoWorkAsync(CancellationToken token) { for (int i = 0; i < 10; i++) { if (token.IsCancellationRequested) { Console.WriteLine("작업이 취소되었습니다."); return; } Console.WriteLine($"작업 진행 중... {i}"); await Task.Delay(1000); } } }
using System;
using System.Threading;
using System.Threading.Tasks;

class Program
{
    static async Task Main()
    {
        CancellationTokenSource cts = new CancellationTokenSource();
        CancellationToken token = cts.Token;

        // 취소 요청 시 실행될 콜백 등록
        token.Register(() => Console.WriteLine("취소 요청이 감지되었습니다!"));

        Task task = DoWorkAsync(token);

        await Task.Delay(3000);
        cts.Cancel(); // 3초 후 취소 요청

        await task;
    }

    static async Task DoWorkAsync(CancellationToken token)
    {
        for (int i = 0; i < 10; i++)
        {
            if (token.IsCancellationRequested)
            {
                Console.WriteLine("작업이 취소되었습니다.");
                return;
            }

            Console.WriteLine($"작업 진행 중... {i}");
            await Task.Delay(1000);
        }
    }
}
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
작업 진행 중... 0
작업 진행 중... 1
작업 진행 중... 2
취소 요청이 감지되었습니다!
작업이 취소되었습니다.
작업 진행 중... 0 작업 진행 중... 1 작업 진행 중... 2 취소 요청이 감지되었습니다! 작업이 취소되었습니다.
작업 진행 중... 0
작업 진행 중... 1
작업 진행 중... 2
취소 요청이 감지되었습니다!
작업이 취소되었습니다.

🔹 async와 CancellationToken을 활용한 HttpClient 요청 취소

HttpClient에서 CancellationToken을 사용하여 HTTP 요청을 중단할 수도 있습니다.

HttpClient 요청이 너무 오래 걸리면 CancellationToken을 이용해 요청을 취소할 수 있습니다.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
using System;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
class Program
{
static async Task Main()
{
using (HttpClient client = new HttpClient())
using (CancellationTokenSource cts = new CancellationTokenSource(TimeSpan.FromSeconds(2))) // 2초 후 자동 취소
{
try
{
HttpResponseMessage response = await client.GetAsync("https://example.com", cts.Token);
Console.WriteLine("응답 수신 완료!");
}
catch (TaskCanceledException)
{
Console.WriteLine("HTTP 요청이 취소되었습니다.");
}
}
}
}
using System; using System.Net.Http; using System.Threading; using System.Threading.Tasks; class Program { static async Task Main() { using (HttpClient client = new HttpClient()) using (CancellationTokenSource cts = new CancellationTokenSource(TimeSpan.FromSeconds(2))) // 2초 후 자동 취소 { try { HttpResponseMessage response = await client.GetAsync("https://example.com", cts.Token); Console.WriteLine("응답 수신 완료!"); } catch (TaskCanceledException) { Console.WriteLine("HTTP 요청이 취소되었습니다."); } } } }
using System;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;

class Program
{
    static async Task Main()
    {
        using (HttpClient client = new HttpClient())
        using (CancellationTokenSource cts = new CancellationTokenSource(TimeSpan.FromSeconds(2))) // 2초 후 자동 취소
        {
            try
            {
                HttpResponseMessage response = await client.GetAsync("https://example.com", cts.Token);
                Console.WriteLine("응답 수신 완료!");
            }
            catch (TaskCanceledException)
            {
                Console.WriteLine("HTTP 요청이 취소되었습니다.");
            }
        }
    }
}

🔹 CancelAfter()를 활용한 자동 취소

일정 시간이 지나면 자동으로 취소되도록 설정 가능 (CancelAfter(milliseconds))

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
cts.Token.Register(() => Console.WriteLine("취소 요청이 감지되었습니다!"));
cts.Token.Register(() => Console.WriteLine("취소 요청이 감지되었습니다!"));
cts.Token.Register(() => Console.WriteLine("취소 요청이 감지되었습니다!"));

사용 예제 (5초 후 자동 취소되는 코드)

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
using System;
using System.Threading;
using System.Threading.Tasks;
class Program
{
static async Task Main()
{
CancellationTokenSource cts = new CancellationTokenSource();
cts.CancelAfter(5000); // 5초 후 자동 취소
try
{
await DoWorkAsync(cts.Token);
}
catch (OperationCanceledException)
{
Console.WriteLine("작업이 시간 초과로 취소되었습니다.");
}
}
static async Task DoWorkAsync(CancellationToken token)
{
for (int i = 0; i < 10; i++)
{
token.ThrowIfCancellationRequested();
Console.WriteLine($"작업 진행 중... {i}");
await Task.Delay(1000);
}
}
}
using System; using System.Threading; using System.Threading.Tasks; class Program { static async Task Main() { CancellationTokenSource cts = new CancellationTokenSource(); cts.CancelAfter(5000); // 5초 후 자동 취소 try { await DoWorkAsync(cts.Token); } catch (OperationCanceledException) { Console.WriteLine("작업이 시간 초과로 취소되었습니다."); } } static async Task DoWorkAsync(CancellationToken token) { for (int i = 0; i < 10; i++) { token.ThrowIfCancellationRequested(); Console.WriteLine($"작업 진행 중... {i}"); await Task.Delay(1000); } } }
using System;
using System.Threading;
using System.Threading.Tasks;

class Program
{
    static async Task Main()
    {
        CancellationTokenSource cts = new CancellationTokenSource();
        cts.CancelAfter(5000); // 5초 후 자동 취소

        try
        {
            await DoWorkAsync(cts.Token);
        }
        catch (OperationCanceledException)
        {
            Console.WriteLine("작업이 시간 초과로 취소되었습니다.");
        }
    }

    static async Task DoWorkAsync(CancellationToken token)
    {
        for (int i = 0; i < 10; i++)
        {
            token.ThrowIfCancellationRequested();
            Console.WriteLine($"작업 진행 중... {i}");
            await Task.Delay(1000);
        }
    }
}

🔹 LinkedTokenSource를 사용하여 여러 토큰을 결합

여러 CancellationTokenSource를 결합하여 하나의 토큰으로 관리 가능

어느 하나라도 취소되면 전체 작업이 취소됨

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
using System;
using System.Threading;
using System.Threading.Tasks;
class Program
{
static async Task Main()
{
CancellationTokenSource cts1 = new CancellationTokenSource();
CancellationTokenSource cts2 = new CancellationTokenSource();
// 두 개의 토큰을 하나로 결합
using (CancellationTokenSource linkedCts = CancellationTokenSource.CreateLinkedTokenSource(cts1.Token, cts2.Token))
{
Task task = DoWorkAsync(linkedCts.Token);
await Task.Delay(3000);
cts1.Cancel(); // 하나의 토큰만 취소해도 작업 전체가 중단됨
await task;
}
}
static async Task DoWorkAsync(CancellationToken token)
{
for (int i = 0; i < 10; i++)
{
token.ThrowIfCancellationRequested();
Console.WriteLine($"작업 진행 중... {i}");
await Task.Delay(1000);
}
}
}
using System; using System.Threading; using System.Threading.Tasks; class Program { static async Task Main() { CancellationTokenSource cts1 = new CancellationTokenSource(); CancellationTokenSource cts2 = new CancellationTokenSource(); // 두 개의 토큰을 하나로 결합 using (CancellationTokenSource linkedCts = CancellationTokenSource.CreateLinkedTokenSource(cts1.Token, cts2.Token)) { Task task = DoWorkAsync(linkedCts.Token); await Task.Delay(3000); cts1.Cancel(); // 하나의 토큰만 취소해도 작업 전체가 중단됨 await task; } } static async Task DoWorkAsync(CancellationToken token) { for (int i = 0; i < 10; i++) { token.ThrowIfCancellationRequested(); Console.WriteLine($"작업 진행 중... {i}"); await Task.Delay(1000); } } }
using System;
using System.Threading;
using System.Threading.Tasks;

class Program
{
    static async Task Main()
    {
        CancellationTokenSource cts1 = new CancellationTokenSource();
        CancellationTokenSource cts2 = new CancellationTokenSource();

        // 두 개의 토큰을 하나로 결합
        using (CancellationTokenSource linkedCts = CancellationTokenSource.CreateLinkedTokenSource(cts1.Token, cts2.Token))
        {
            Task task = DoWorkAsync(linkedCts.Token);

            await Task.Delay(3000);
            cts1.Cancel(); // 하나의 토큰만 취소해도 작업 전체가 중단됨

            await task;
        }
    }

    static async Task DoWorkAsync(CancellationToken token)
    {
        for (int i = 0; i < 10; i++)
        {
            token.ThrowIfCancellationRequested();
            Console.WriteLine($"작업 진행 중... {i}");
            await Task.Delay(1000);
        }
    }
}

결과적으로 cts1.Cancel(); 호출 시 linkedCts도 취소됨

🔹 Parallel.ForEach()에서 CancellationToken 적용

Parallel.ForEach()에서 CancellationToken을 사용하여 병렬 처리 도중 특정 조건에서 중단할 수도 있습니다.

ParallelOptions을 통해 취소 토큰을 설정하면 병렬 처리에서도 취소를 손쉽게 관리할 수 있습니다.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
ParallelOptions options = new ParallelOptions
{
CancellationToken = cts.Token,
MaxDegreeOfParallelism = 4 // 최대 4개의 스레드 사용
};
try
{
Parallel.ForEach(Enumerable.Range(1, 100), options, (num, state) =>
{
options.CancellationToken.ThrowIfCancellationRequested();
Console.WriteLine($"Processing {num}");
Thread.Sleep(500); // 가상의 작업
});
}
catch (OperationCanceledException)
{
Console.WriteLine("병렬 작업이 취소되었습니다.");
}
ParallelOptions options = new ParallelOptions { CancellationToken = cts.Token, MaxDegreeOfParallelism = 4 // 최대 4개의 스레드 사용 }; try { Parallel.ForEach(Enumerable.Range(1, 100), options, (num, state) => { options.CancellationToken.ThrowIfCancellationRequested(); Console.WriteLine($"Processing {num}"); Thread.Sleep(500); // 가상의 작업 }); } catch (OperationCanceledException) { Console.WriteLine("병렬 작업이 취소되었습니다."); }
ParallelOptions options = new ParallelOptions
{
    CancellationToken = cts.Token,
    MaxDegreeOfParallelism = 4 // 최대 4개의 스레드 사용
};

try
{
    Parallel.ForEach(Enumerable.Range(1, 100), options, (num, state) =>
    {
        options.CancellationToken.ThrowIfCancellationRequested();
        Console.WriteLine($"Processing {num}");
        Thread.Sleep(500); // 가상의 작업
    });
}
catch (OperationCanceledException)
{
    Console.WriteLine("병렬 작업이 취소되었습니다.");
}

✅ 주의 사항

🔹 리소스 정리의 중요성

취소된 작업에서도 적절한 리소스 정리가 필요합니다.

try/finally 블록이나 using 문을 적절이 활용하는 것이 좋습니다.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
static async Task ProcessFileAsync(string path, CancellationToken token)
{
FileStream file = null;
try
{
file = new FileStream(path, FileMode.Open);
// 파일 처리 작업
token.ThrowIfCancellationRequested();
}
catch (OperationCanceledException)
{
Console.WriteLine("파일 처리가 취소되었습니다.");
throw;
}
finally
{
// 취소 여부와 상관없이 항상 실행됨
file?.Dispose();
}
}
static async Task ProcessFileAsync(string path, CancellationToken token) { FileStream file = null; try { file = new FileStream(path, FileMode.Open); // 파일 처리 작업 token.ThrowIfCancellationRequested(); } catch (OperationCanceledException) { Console.WriteLine("파일 처리가 취소되었습니다."); throw; } finally { // 취소 여부와 상관없이 항상 실행됨 file?.Dispose(); } }
static async Task ProcessFileAsync(string path, CancellationToken token)
{
    FileStream file = null;
    try
    {
        file = new FileStream(path, FileMode.Open);
        // 파일 처리 작업
        token.ThrowIfCancellationRequested();
    }
    catch (OperationCanceledException)
    {
        Console.WriteLine("파일 처리가 취소되었습니다.");
        throw;
    }
    finally
    {
        // 취소 여부와 상관없이 항상 실행됨
        file?.Dispose();
    }
}

🔹 CancellationToken 확인 빈도

장시간 실행되는 작업에서는 적절한 간격으로 취소 토큰을 확인하는 것이 중요합니다.

취소 토큰 확인은 가벼운 연산이지만, 매우 빈번하게 호출되는 코드에서는 성능에 영향을 줄 수 있습니다.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
static void ProcessLargeData(IEnumerable<int> items, CancellationToken token)
{
int count = 0;
foreach (var item in items)
{
// 100개 항목마다 취소 여부 확인 (너무 자주 확인하면 성능 저하)
if (count++ % 100 == 0 && token.IsCancellationRequested)
{
Console.WriteLine("작업이 취소되었습니다.");
return;
}
// 항목 처리...
}
}
static void ProcessLargeData(IEnumerable<int> items, CancellationToken token) { int count = 0; foreach (var item in items) { // 100개 항목마다 취소 여부 확인 (너무 자주 확인하면 성능 저하) if (count++ % 100 == 0 && token.IsCancellationRequested) { Console.WriteLine("작업이 취소되었습니다."); return; } // 항목 처리... } }
static void ProcessLargeData(IEnumerable<int> items, CancellationToken token)
{
    int count = 0;
    foreach (var item in items)
    {
        // 100개 항목마다 취소 여부 확인 (너무 자주 확인하면 성능 저하)
        if (count++ % 100 == 0 && token.IsCancellationRequested)
        {
            Console.WriteLine("작업이 취소되었습니다.");
            return;
        }
        
        // 항목 처리...
    }
}

🔹 CancellationTokenSource.Dispose() 중요성

CancellationTokenSource는 사용 후 수명이 끝나면 반드시 Dispose()를 호출해야 합니다. (메모리 누수)

using 문을 사용하는 것을 추천합니다.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
using (var cts = new CancellationTokenSource())
{
// 작업 수행
}
// 여기서 자동으로 cts.Dispose() 호출됨
using (var cts = new CancellationTokenSource()) { // 작업 수행 } // 여기서 자동으로 cts.Dispose() 호출됨
using (var cts = new CancellationTokenSource())
{
    // 작업 수행
}
// 여기서 자동으로 cts.Dispose() 호출됨

✅ 공식 문서 사용 예제

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
// https://learn.microsoft.com/ko-kr/dotnet/api/system.threading.cancellationtokensource?view=net-9.0
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
public class Example
{
public static void Main()
{
// Define the cancellation token.
CancellationTokenSource source = new CancellationTokenSource();
CancellationToken token = source.Token;
Random rnd = new Random();
Object lockObj = new Object();
List<Task<int[]>> tasks = new List<Task<int[]>>();
TaskFactory factory = new TaskFactory(token);
for (int taskCtr = 0; taskCtr <= 10; taskCtr++) {
int iteration = taskCtr + 1;
tasks.Add(factory.StartNew( () => {
int value;
int[] values = new int[10];
for (int ctr = 1; ctr <= 10; ctr++) {
lock (lockObj) {
value = rnd.Next(0,101);
}
if (value == 0) {
source.Cancel();
Console.WriteLine("Cancelling at task {0}", iteration);
break;
}
values[ctr-1] = value;
}
return values;
}, token));
}
try {
Task<double> fTask = factory.ContinueWhenAll(tasks.ToArray(),
(results) => {
Console.WriteLine("Calculating overall mean...");
long sum = 0;
int n = 0;
foreach (var t in results) {
foreach (var r in t.Result) {
sum += r;
n++;
}
}
return sum/(double) n;
} , token);
Console.WriteLine("The mean is {0}.", fTask.Result);
}
catch (AggregateException ae) {
foreach (Exception e in ae.InnerExceptions) {
if (e is TaskCanceledException)
Console.WriteLine("Unable to compute mean: {0}",
((TaskCanceledException) e).Message);
else
Console.WriteLine("Exception: " + e.GetType().Name);
}
}
finally {
source.Dispose();
}
}
}
// Repeated execution of the example produces output like the following:
// Cancelling at task 5
// Unable to compute mean: A task was canceled.
//
// Cancelling at task 10
// Unable to compute mean: A task was canceled.
//
// Calculating overall mean...
// The mean is 5.29545454545455.
//
// Cancelling at task 4
// Unable to compute mean: A task was canceled.
//
// Cancelling at task 5
// Unable to compute mean: A task was canceled.
//
// Cancelling at task 6
// Unable to compute mean: A task was canceled.
//
// Calculating overall mean...
// The mean is 4.97363636363636.
//
// Cancelling at task 4
// Unable to compute mean: A task was canceled.
//
// Cancelling at task 5
// Unable to compute mean: A task was canceled.
//
// Cancelling at task 4
// Unable to compute mean: A task was canceled.
//
// Calculating overall mean...
// The mean is 4.86545454545455.
// https://learn.microsoft.com/ko-kr/dotnet/api/system.threading.cancellationtokensource?view=net-9.0 using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; public class Example { public static void Main() { // Define the cancellation token. CancellationTokenSource source = new CancellationTokenSource(); CancellationToken token = source.Token; Random rnd = new Random(); Object lockObj = new Object(); List<Task<int[]>> tasks = new List<Task<int[]>>(); TaskFactory factory = new TaskFactory(token); for (int taskCtr = 0; taskCtr <= 10; taskCtr++) { int iteration = taskCtr + 1; tasks.Add(factory.StartNew( () => { int value; int[] values = new int[10]; for (int ctr = 1; ctr <= 10; ctr++) { lock (lockObj) { value = rnd.Next(0,101); } if (value == 0) { source.Cancel(); Console.WriteLine("Cancelling at task {0}", iteration); break; } values[ctr-1] = value; } return values; }, token)); } try { Task<double> fTask = factory.ContinueWhenAll(tasks.ToArray(), (results) => { Console.WriteLine("Calculating overall mean..."); long sum = 0; int n = 0; foreach (var t in results) { foreach (var r in t.Result) { sum += r; n++; } } return sum/(double) n; } , token); Console.WriteLine("The mean is {0}.", fTask.Result); } catch (AggregateException ae) { foreach (Exception e in ae.InnerExceptions) { if (e is TaskCanceledException) Console.WriteLine("Unable to compute mean: {0}", ((TaskCanceledException) e).Message); else Console.WriteLine("Exception: " + e.GetType().Name); } } finally { source.Dispose(); } } } // Repeated execution of the example produces output like the following: // Cancelling at task 5 // Unable to compute mean: A task was canceled. // // Cancelling at task 10 // Unable to compute mean: A task was canceled. // // Calculating overall mean... // The mean is 5.29545454545455. // // Cancelling at task 4 // Unable to compute mean: A task was canceled. // // Cancelling at task 5 // Unable to compute mean: A task was canceled. // // Cancelling at task 6 // Unable to compute mean: A task was canceled. // // Calculating overall mean... // The mean is 4.97363636363636. // // Cancelling at task 4 // Unable to compute mean: A task was canceled. // // Cancelling at task 5 // Unable to compute mean: A task was canceled. // // Cancelling at task 4 // Unable to compute mean: A task was canceled. // // Calculating overall mean... // The mean is 4.86545454545455.
// https://learn.microsoft.com/ko-kr/dotnet/api/system.threading.cancellationtokensource?view=net-9.0
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;

public class Example
{
   public static void Main()
   {
      // Define the cancellation token.
      CancellationTokenSource source = new CancellationTokenSource();
      CancellationToken token = source.Token;

      Random rnd = new Random();
      Object lockObj = new Object();
      
      List<Task<int[]>> tasks = new List<Task<int[]>>();
      TaskFactory factory = new TaskFactory(token);
      for (int taskCtr = 0; taskCtr <= 10; taskCtr++) {
         int iteration = taskCtr + 1;
         tasks.Add(factory.StartNew( () => {
           int value;
           int[] values = new int[10];
           for (int ctr = 1; ctr <= 10; ctr++) {
              lock (lockObj) {
                 value = rnd.Next(0,101);
              }
              if (value == 0) { 
                 source.Cancel();
                 Console.WriteLine("Cancelling at task {0}", iteration);
                 break;
              }   
              values[ctr-1] = value; 
           }
           return values;
        }, token));   
      }
      try {
         Task<double> fTask = factory.ContinueWhenAll(tasks.ToArray(), 
         (results) => {
            Console.WriteLine("Calculating overall mean...");
            long sum = 0;
            int n = 0; 
            foreach (var t in results) {
               foreach (var r in t.Result) {
                  sum += r;
                  n++;
               }
            }
            return sum/(double) n;
         } , token);
         Console.WriteLine("The mean is {0}.", fTask.Result);
      }   
      catch (AggregateException ae) {
         foreach (Exception e in ae.InnerExceptions) {
            if (e is TaskCanceledException)
               Console.WriteLine("Unable to compute mean: {0}", 
                  ((TaskCanceledException) e).Message);
            else
               Console.WriteLine("Exception: " + e.GetType().Name);
         }
      }
      finally {
         source.Dispose();
      }
   }
}
// Repeated execution of the example produces output like the following:
//       Cancelling at task 5
//       Unable to compute mean: A task was canceled.
//       
//       Cancelling at task 10
//       Unable to compute mean: A task was canceled.
//       
//       Calculating overall mean...
//       The mean is 5.29545454545455.
//       
//       Cancelling at task 4
//       Unable to compute mean: A task was canceled.
//       
//       Cancelling at task 5
//       Unable to compute mean: A task was canceled.
//       
//       Cancelling at task 6
//       Unable to compute mean: A task was canceled.
//       
//       Calculating overall mean...
//       The mean is 4.97363636363636.
//       
//       Cancelling at task 4
//       Unable to compute mean: A task was canceled.
//       
//       Cancelling at task 5
//       Unable to compute mean: A task was canceled.
//       
//       Cancelling at task 4
//       Unable to compute mean: A task was canceled.
//       
//       Calculating overall mean...
//       The mean is 4.86545454545455.

댓글 달기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다