본문 바로가기
카테고리 없음

여러 비동기 작업을 동시에 수행하면서 특정 작업만 멈추는 방법

by keisoft 2024. 8. 5.

네, 여러 비동기 작업을 동시에 수행하면서 특정 작업만 멈추는 방법에 대해 설명드리겠습니다. 이를 구현하기 위해 각 작업별로 고유한 식별자를 사용하고, 작업별로 `CancellationTokenSource`를 관리하는 방식을 사용할 수 있습니다.

다음은 이를 구현한 예시 코드입니다:



```csharp
using System;
using System.Collections.Concurrent;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;

[ApiController]
[Route("api/[controller]")]
public class TaskController : ControllerBase
{
    private static readonly ConcurrentDictionary<string, CancellationTokenSource> _tasks = new ConcurrentDictionary<string, CancellationTokenSource>();

    [HttpPost("start")]
    public async Task<IActionResult> StartTask([FromBody] TaskRequest request)
    {
        var taskId = Guid.NewGuid().ToString();
        var cts = new CancellationTokenSource();

        if (_tasks.TryAdd(taskId, cts))
        {
            // 비동기로 작업 시작
            _ = RunLongTask(taskId, request.TaskType, cts.Token);
            return Ok(new { TaskId = taskId, Message = "작업이 시작되었습니다." });
        }

        return BadRequest("작업을 시작할 수 없습니다.");
    }

    [HttpPost("stop/{taskId}")]
    public IActionResult StopTask(string taskId)
    {
        if (_tasks.TryRemove(taskId, out var cts))
        {
            cts.Cancel();
            return Ok($"작업 {taskId}가 중지되었습니다.");
        }

        return NotFound($"작업 {taskId}를 찾을 수 없습니다.");
    }

    private async Task RunLongTask(string taskId, string taskType, CancellationToken cancellationToken)
    {
        try
        {
            Console.WriteLine($"작업 {taskId} ({taskType}) 시작됨");

            while (!cancellationToken.IsCancellationRequested)
            {
                // 실제 작업 수행
                await Task.Delay(1000, cancellationToken);
                Console.WriteLine($"작업 {taskId} ({taskType}) 실행 중...");
            }
        }
        catch (OperationCanceledException)
        {
            Console.WriteLine($"작업 {taskId} ({taskType}) 취소됨");
        }
        finally
        {
            _tasks.TryRemove(taskId, out _);
            Console.WriteLine($"작업 {taskId} ({taskType}) 종료됨");
        }
    }
}

public class TaskRequest
{
    public string TaskType { get; set; }
}
```

이 구현의 주요 특징은 다음과 같습니다:

1. `ConcurrentDictionary`를 사용하여 여러 작업의 `CancellationTokenSource`를 관리합니다. 키는 작업의 고유 ID입니다.

2. `StartTask` API:
   - 새로운 GUID를 생성하여 작업 ID로 사용합니다.
   - 새 `CancellationTokenSource`를 생성하고 딕셔너리에 추가합니다.
   - 비동기 작업을 시작하고 작업 ID를 반환합니다.

3. `StopTask` API:
   - 주어진 작업 ID에 해당하는 `CancellationTokenSource`를 찾아 취소합니다.
   - 작업이 존재하지 않으면 NotFound를 반환합니다.

4. `RunLongTask` 메서드:
   - 실제 장기 실행 작업을 시뮬레이션합니다.
   - 취소 토큰을 주기적으로 확인하여 작업 취소 요청을 처리합니다.
   - 작업이 완료되거나 취소되면 딕셔너리에서 해당 작업을 제거합니다.

이 방식을 사용하면 여러 비동기 작업을 동시에 실행하고 각각을 개별적으로 관리할 수 있습니다. 클라이언트는 시작 API를 호출하여 작업 ID를 받고, 이 ID를 사용하여 나중에 특정 작업만 중지할 수 있습니다.

실제 사용 시 고려할 사항:
1. 에러 처리를 더 강화하세요.
2. 작업 상태 모니터링 기능을 추가하는 것이 좋습니다.
3. 장기 실행 작업의 결과를 저장하고 검색하는 메커니즘을 구현하세요.
4. 보안 측면에서 작업 ID의 유효성을 검증하는 로직을 추가하세요.

이 구현에 대해 더 자세한 설명이 필요하거나 특정 부분에 대해 질문이 있으시면 말씀해 주세요.