[C#] TPL 작업을 중단 / 취소하려면 어떻게합니까?

스레드에서 일부 System.Threading.Task작업을 만들고 각 작업을 시작합니다.

.Abort()스레드를 죽이려고 할 때 작업이 중단되지 않습니다.

.Abort()내 작업에 어떻게 전송 합니까?



답변

당신은 할 수 없습니다. 작업은 스레드 풀의 백그라운드 스레드를 사용합니다. 또한 Abort 메서드를 사용하여 스레드를 취소하지 않는 것이 좋습니다. 취소 토큰을 사용하여 작업을 취소하는 적절한 방법을 설명하는 다음 블로그 게시물 을 살펴볼 수 있습니다 . 예를 들면 다음과 같습니다.

class Program
{
    static void Main()
    {
        var ts = new CancellationTokenSource();
        CancellationToken ct = ts.Token;
        Task.Factory.StartNew(() =>
        {
            while (true)
            {
                // do some heavy work here
                Thread.Sleep(100);
                if (ct.IsCancellationRequested)
                {
                    // another thread decided to cancel
                    Console.WriteLine("task canceled");
                    break;
                }
            }
        }, ct);

        // Simulate waiting 3s for the task to complete
        Thread.Sleep(3000);

        // Can't wait anymore => cancel this task 
        ts.Cancel();
        Console.ReadLine();
    }
}


답변

작업이 실행중인 스레드를 캡처하면 작업을 중단 할 수 있습니다. 다음은이를 보여주는 예제 코드입니다.

void Main()
{
    Thread thread = null;

    Task t = Task.Run(() =>
    {
        //Capture the thread
        thread = Thread.CurrentThread;

        //Simulate work (usually from 3rd party code)
        Thread.Sleep(1000);

        //If you comment out thread.Abort(), then this will be displayed
        Console.WriteLine("Task finished!");
    });

    //This is needed in the example to avoid thread being still NULL
    Thread.Sleep(10);

    //Cancel the task by aborting the thread
    thread.Abort();
}

필자는 Task.Run ()을 사용하여 가장 일반적인 유스 케이스를 보여주었습니다. 취소 여부를 결정하기 위해 CancellationTokenSource 클래스를 사용하지 않는 오래된 단일 스레드 코드가있는 작업의 편의를 사용했습니다.


답변

마찬가지로 이 게시물 제안, 이것은 다음과 같은 방법으로 수행 할 수 있습니다 :

int Foo(CancellationToken token)
{
    Thread t = Thread.CurrentThread;
    using (token.Register(t.Abort))
    {
        // compute-bound work here
    }
}

작동하지만 그러한 방법을 사용하지 않는 것이 좋습니다. 작업에서 실행되는 코드를 제어 할 수 있으면 적절한 취소 처리를 수행하는 것이 좋습니다.


답변

이런 종류의 일은 왜 Abort더 이상 사용되지 않는 물류상의 이유 중 하나입니다 . 우선, 가능하면 스레드를 취소하거나 중지하는 데 사용하지 마십시오 Thread.Abort(). Abort()적시에 중지하라는보다 평화로운 요청에 응답하지 않는 스레드를 강제로 종료하는 데만 사용해야합니다.

즉, 한 스레드가 설정하고 대기하는 동안 다른 스레드가 주기적으로 확인하고 정상적으로 종료되는 공유 취소 표시기를 제공해야합니다. .NET 4에는이 목적을 위해 특별히 설계된 구조가 포함되어 CancellationToken있습니다.


답변

직접 시도하지 마십시오. CancellationToken 과 함께 작동하도록 작업을 설계하십시오. 하고이 방법으로 취소하십시오.

또한 CancellationToken을 통해 기본 스레드를 작동하도록 변경하는 것이 좋습니다. 전화 Thread.Abort()하는 것은 나쁜 생각입니다. 진단하기가 매우 어려운 다양한 문제가 발생할 수 있습니다. 대신, 해당 스레드는 작업에서 사용하는 것과 동일한 취소 를 사용할 CancellationTokenSource수 있으며 모든 작업 및 기본 스레드 의 취소를 트리거하는 데 사용될 수 있습니다 .

이것은 훨씬 더 단순하고 안전한 디자인으로 이어질 것입니다.


답변

Task.Factory.StartNew ()에서 익명 메서드를 사용하지 않을 때 CancellationTokens를 사용하는 방법에 대한 Prerak K의 질문에 대답하기 위해 MSDN 예제에 표시된 것처럼 CancellationToken을 StartNew ()로 시작하는 메서드에 매개 변수로 전달합니다. 여기 .

예 :

var tokenSource = new CancellationTokenSource();
var token = tokenSource.Token;

Task.Factory.StartNew( () => DoSomeWork(1, token), token);

static void DoSomeWork(int taskNum, CancellationToken ct)
{
    // Do work here, checking and acting on ct.IsCancellationRequested where applicable, 

}


답변

작업을 취소하기 위해 혼합 접근법을 사용합니다.

  • 첫째, 나는 사용하여 정중를 취소하기 위해 노력하고있어 예약 취소 .
  • 여전히 실행 중이면 (예 : 개발자의 실수로 인해) 구식 Abort 메소드를 사용하여 오작동하고 종료 합니다.

아래 예를 확인하십시오.

private CancellationTokenSource taskToken;
private AutoResetEvent awaitReplyOnRequestEvent = new AutoResetEvent(false);

void Main()
{
    // Start a task which is doing nothing but sleeps 1s
    LaunchTaskAsync();
    Thread.Sleep(100);
    // Stop the task
    StopTask();
}

/// <summary>
///     Launch task in a new thread
/// </summary>
void LaunchTaskAsync()
{
    taskToken = new CancellationTokenSource();
    Task.Factory.StartNew(() =>
        {
            try
            {   //Capture the thread
                runningTaskThread = Thread.CurrentThread;
                // Run the task
                if (taskToken.IsCancellationRequested || !awaitReplyOnRequestEvent.WaitOne(10000))
                    return;
                Console.WriteLine("Task finished!");
            }
            catch (Exception exc)
            {
                // Handle exception
            }
        }, taskToken.Token);
}

/// <summary>
///     Stop running task
/// </summary>
void StopTask()
{
    // Attempt to cancel the task politely
    if (taskToken != null)
    {
        if (taskToken.IsCancellationRequested)
            return;
        else
            taskToken.Cancel();
    }

    // Notify a waiting thread that an event has occurred
    if (awaitReplyOnRequestEvent != null)
        awaitReplyOnRequestEvent.Set();

    // If 1 sec later the task is still running, kill it cruelly
    if (runningTaskThread != null)
    {
        try
        {
            runningTaskThread.Join(TimeSpan.FromSeconds(1));
        }
        catch (Exception ex)
        {
            runningTaskThread.Abort();
        }
    }
}