.NET Documentation์ Task Cancellation ๋ฒ์ญ ๋ฐ ๋ด์ฉ ์ถ๊ฐํ์ฌ ์ ๋ฆฌํ ๊ธ
์์ ์ทจ์ํ๊ธฐ
Task์ Task<TResult> ํด๋์ค์์ ์ทจ์ ํ ํฐ์ ์ฌ์ฉํ์ฌ Task๋ฅผ ์ทจ์ํ ์ ์์ต๋๋ค. (๊ด๋ฆฌ๋๋ ์ค๋ ๋ ์ทจ์ํ๊ธฐ ์ฐธ๊ณ ) Task ํด๋์ค์์ Cancellation์ ์คํ ์ค ์ทจ์๋ delegate(Task๋ก ๋ฑ๋ก๋ ์คํ ํจ์์ ์ฐธ์กฐ)์ ์ทจ์๋ฅผ ์์ฒญํ ์ฝ๋์ ์ํธ ์์ฉ์ผ๋ก ์งํ๋ฉ๋๋ค. ์ทจ์ ์์ฒญ ์ฝ๋์์ CancellationTokenSource.Cancel() ๋ฉ์๋๋ฅผ ํธ์ถํ ๋ค ์ฌ์ฉ์ delegate๊ฐ ์ํ๋ ์์ ์ ์ข ๋ฃ๋์๋ค๋ฉด, ์์ ์ด ์ฑ๊ณต์ ์ผ๋ก ์ทจ์๋ ๊ฒ์ ๋๋ค.
์์ ์ข ๋ฃํ๊ธฐ๋ ๋ค์๊ณผ ๊ฐ์ ๋ ๊ฐ์ง ๋ฐฉ๋ฒ์ด ์์ต๋๋ค.
๋๋ถ๋ถ์ ๊ฒฝ์ฐ delegate๊ฐ return์ ๋๋ฌํด ์ข ๋ฃ๋๋ ์ผ๋ฐ์ ์ธ ๋ฐฉ์์ ์ฌ์ฉํฉ๋๋ค. ๊ทธ๋ฌ๋ ์ด๋ ๊ฒ ์ข ๋ฃ๋ ์์ ์ธ์คํด์ค๋ Canceled(์ทจ์)๊ฐ ์๋๋ผ RanToCompletion(์ฑ๊ณต์ ์ผ๋ก ์ข ๋ฃ) ์ํ๋ก ์ ํ๋ฉ๋๋ค.
delegate๋ฅผ ์ทจ์ ์ํ๋ก ๋ง๋ค๊ธฐ ์ํด์๋ ์คํ ์ทจ์ ์์ธ๋ฅผ ๋ฐ์์์ผ ์ทจ์๊ฐ ์์ฒญ๋ ํ ํฐ์ delegate์ ์ ๋ฌํ๋ฉด ๋ฉ๋๋ค. ๋ณดํต ThrowIfCancellationRequested() ๋ฉ์๋๋ฅผ ์ฌ์ฉํฉ๋๋ค. ์ดํ, ์ทจ์ ์์ฒญ(์คํ ์ทจ์ ์์ธ)์ ๋ณด๋ธ ์ฝ๋์์ delegate์ ์ํ๊ฐ ์ทจ์๊ฐ ๋ ๋๊น์ง ๋ชจ๋ํฐ๋งํ๋ ๊ตฌ์กฐ์ ๋๋ค.
์๋๋ ์คํ ์ทจ์ ์์ธ๋ฅผ ์ฌ์ฉํ๋ Task ์ทจ์ ๋ฐฉ์์ ๊ธฐ๋ณธ์ ์ธ ์์ ์ ๋๋ค. ํ ํฐ์ delegate์ ์์ ์ธ์คํด์ค์ ์ ๋ฌ๋ฉ๋๋ค.
using System;
using System.Threading;
using System.Threading.Tasks;
class Program
{
static async Task Main()
{
var tokenSource2 = new CancellationTokenSource();
CancellationToken ct = tokenSource2.Token;
var task = Task.Run(() =>
// Task๋ก ์คํํ delegate ์ ์
{
// ์ด๋ฏธ ์ทจ์๋ ์์
์ธ์ง ํ์ธ
ct.ThrowIfCancellationRequested();
bool moreToDo = true;
while (moreToDo)
{
// ์์ธ์ฒ๋ฆฌ ์ ์งํํด์ผํ๋ ์ด๊ธฐํ ์์
์ด ์๋ค๋ฉด
// IsCancellationRequested ๊ฐ์ ์ฒดํฌํ๋ If๋ฌธ ์ถ๊ฐ
if (ct.IsCancellationRequested)
{
// ์ด๊ธฐํ ์์
์ ์งํํ ๋ค...
ct.ThrowIfCancellationRequested();
}
}
}, tokenSource2.Token); // Task์๋ ๊ฐ์ ํ ํฐ์ ์ ๋ฌ
// ์ฝ๋์์ ์ทจ์ ์์ฒญ ์คํ!
tokenSource2.Cancel();
// ํด๋น ์ค๋ ๋์์ ๊ณ์ ์งํํด๋ ๋๊ณ ,
// try-catch๋ฌธ์์ ์์
(task)์ ๋๊ธฐํด๋ ๋๋ค.
try
{
await task;
}
catch (OperationCanceledException e)
{
Console.WriteLine($"{nameof(OperationCanceledException)} thrown with message: {e.Message}");
}
finally
{
tokenSource2.Dispose();
}
Console.ReadKey();
}
}
Task์ child Task๋ฅผ ์ทจ์ํ๋ ๋ฐฉ๋ฒ์ด๋ผ๋ ๊ธ์์ ์์ธํ ์์ ๋ฅผ ํ์ธํ ์ ์์ต๋๋ค.
์์ ์ธ์คํด์ค๋ ์ฌ์ฉ์ ์ฝ๋๊ฐ ์คํ ์ทจ์ ์์ธ๋ฅผ ๋ณด๋ด๋ฉด ๋ฐ์ ํ ํฐ๊ณผ ์์ ์ด ์ฐ๊ฒฐ๋์ด ์๋(์์ ์์ฑ ์ ์ธ์๋ก ๋ฐ์) ํ ํฐ์ ๋น๊ตํฉ๋๋ค. ๋ง์ฝ ๋ ํ ํฐ์ด ์๋ก ๊ฐ๊ณ ํ ํฐ์ IsCancellationRequested ๊ฐ์ด true์ด๋ฉด, ์ ํจํ ์์ฒญ์ผ๋ก ํด์ํ๊ณ Task๋ฅผ ์ทจ์ ์ํ๋ก ์ ํํฉ๋๋ค.
๊ทธ๋ฌ๋ Wait์ด๋ WaitAll ๋๋ฌธ์ ์์ ๋๊ธฐ๊ฐ ์งํ๋๋ค๋ฉด, ์์ ์ทจ์ ์์ธ๊ฐ ์คํ๋ฉ๋๋ค. ์ด ์์ธ๋ ๋ฌธ์ ๊ฐ ์๊ฒผ๋ค๋ ๊ฒ์ ์๋ฏธํ๋ ๊ฒ์ด ์๋๋ผ ์ทจ์๊ฐ ์ฑ๊ณต์ ์ผ๋ก ์์๋์๋ค๋ ๊ฒ์ ์๋ฏธํฉ๋๋ค. ๋ฐ๋ผ์ Task์ ์์ธ ๊ฐ์ null์ ๋๋ค.
๋ง์ฝ ์์ธ๊ฐ ๊ฐ์ง๋ ํ ํฐ๊ณผ Task์ ํ ํฐ์ด ์๋ก ๋ค๋ฅด๊ฑฐ๋ ํ ํฐ์ IsCancellationRequested๊ฐ์ด false๋ผ๋ฉด, ์์ธ๋ ์ผ๋ฐ์ ์ธ ์์ธ๋ก ์ทจ๊ธ๋ฉ๋๋ค. ๋ฐ๋ผ์ Task๋ ์คํจ ์ํ๋ก ์ ํ๋ฉ๋๋ค. ์์๋ Task์ ์ํ๋ Status ํ๋กํผํฐ์์ ํ์ธํ ์ ์์ต๋๋ค.
์ทจ์ ์์ฒญ์ ๋ฐ์์ง๋ผ๋ Task๊ฐ ์ผ๋ถ ํญ๋ชฉ์ ๊ณ์ ์ฒ๋ฆฌํ๋ ๊ฒฝ์ฐ๊ฐ ์์ ์ ์์ต๋๋ค.
4์ค ์์ฝ
1. TokenSource์์ ์ทจ์ ํ ํฐ์ ์์ฑํ๊ณ , ์ด ํ ํฐ์ Task์ ์ด Task์์ ์คํํ delegate์ ์ ๋ฌํ๋ค.
2. delegate์๋ ๋ฐ๊ธ๋ฐ์ ์ทจ์ ํ ํฐ์ ์ํ๋ฅผ ๋ชจ๋ํฐ๋งํ๋ ๊ตฌ๋ฌธ์ ์ถ๊ฐํ๋ค.
3. delegate๊ฐ ์ข ๋ฃ๋๊ธฐ ์ํ๋ ์์ ์์ ์ทจ์ ํ ํฐ ์์ธ๋ฅผ ๋์ง๋ ๊ตฌ๋ฌธ์ ์ถ๊ฐํ๋ค.
4. Task๊ฐ ์คํ๋๋ Level(3์ด ์๋ ๋ ๋ฒจ)์ ์ฝ๋์์๋ (try-catch๋ฌธ ๋ฑ์ผ๋ก) ํ ํฐ์ ์์ธ๋ฅผ ์ฒดํฌํ๋ค๊ฐ, ์์ธ๊ฐ ์๊ธฐ๋ฉด TokenSource๋ฅผ ํ๊ธฐํ๋๋ก ํ๋ค.
Dispose vs Cancel
์ทจ์ ํ ํฐ ์์ค๋ฅผ Dispose ํ๊ฑฐ๋ Cancel ํ๊ฑฐ๋, ๋ ๋ค Task๋ฅผ ์ข ๋ฃํ๊ณ Cancel ์ํ๋ก ์ ํ์ํจ๋ค. ๊ทธ๋ฐ๋ฐ ๊ตณ์ด ํ ํฐ ์์ค๋ฅผ Cancel ํ๋ ์ด์ ๊ฐ ๋ญ๊น.
ํ๋ฒ ํ ํฐ์ ๋ง๋ ๋ค TokenSource๋ฅผ ๋ ์ฌ์ฉํ์ง ์๋๋ค๋ฉด ํฌ๊ฒ ์๊ด์ด ์๋ค. ๊ทธ๋ฌ๋ Source๊ฐ ํ ํฐ์ ๊ณ์ ๋ง๋ค ์๋ ์๋ ์ํฉ์์ Dispose๋ก Source๋ฅผ ํ๊ธฐํ๋ค๋ฉด, Dispose ์คํ๊ณผ ํ ํฐ ์์ฑ ์์ ๊ฐ Race Condition์ด ๋ฐ์ํ ์ ์๋ค. ์ด๋ฐ ๊ฒฝ์ฐ๋ฅผ ๋ฐฉ์งํ๊ธฐ ์ํ ์ ์ฒ๋ฆฌ ํ์ธ ์๋จ์ผ๋ก Cancel ์์ ์ด ์กด์ฌํ๋ค. (์ด ๋ฌธ๋จ์ ๋ด์ฉ์ ํ๋ฆด ์๋ ์์)
์ฐธ๊ณ ์๋ฃ