programing tip

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

itbloger 2020. 6. 17. 19:01
반응형

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, 

}

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

  • 먼저, Cancellation 을 사용하여 정중하게 취소하려고합니다 .
  • 여전히 실행 중이면 (예 : 개발자의 실수로 인해) 구식 중단 방법을 사용하여 오작동하고 종료 합니다.

아래 예를 확인하십시오.

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

You can use a CancellationToken to control whether the task gets cancelled. Are you talking about aborting it before it's started ("nevermind, I already did this"), or actually interrupting it in middle? If the former, the CancellationToken can be helpful; if the latter, you will probably need to implement your own "bail out" mechanism and check at appropriate points in the task execution whether you should fail fast (you can still use the CancellationToken to help you, but it's a little more manual).

MSDN has an article about cancelling Tasks: http://msdn.microsoft.com/en-us/library/dd997396.aspx


Tasks have first class support for cancellation via cancellation tokens. Create your tasks with cancellation tokens, and cancel the tasks via these explicitly.


Task are being executed on the ThreadPool (at least, if you are using the default factory), so aborting the thread cannot affect the tasks. For aborting tasks, see Task Cancellation on msdn.


I tried CancellationTokenSource but i can't do this. And i did do this with my own way. And it works.

namespace Blokick.Provider
{
    public class SignalRConnectProvider
    {
        public SignalRConnectProvider()
        {
        }

        public bool IsStopRequested { get; set; } = false; //1-)This is important and default `false`.

        public async Task<string> ConnectTab()
        {
            string messageText = "";
            for (int count = 1; count < 20; count++)
            {
                if (count == 1)
                {
                //Do stuff.
                }

                try
                {
                //Do stuff.
                }
                catch (Exception ex)
                {
                //Do stuff.
                }
                if (IsStopRequested) //3-)This is important. The control of the task stopping request. Must be true and in inside.
                {
                    return messageText = "Task stopped."; //4-) And so return and exit the code and task.
                }
                if (Connected)
                {
                //Do stuff.
                }
                if (count == 19)
                {
                //Do stuff.
                }
            }
            return messageText;
        }
    }
}

And another class of the calling the method:

namespace Blokick.Views
{
    [XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class MessagePerson : ContentPage
    {
        SignalRConnectProvider signalR = new SignalRConnectProvider();

        public MessagePerson()
        {
            InitializeComponent();

            signalR.IsStopRequested = true; // 2-) And this. Make true if running the task and go inside if statement of the IsStopRequested property.

            if (signalR.ChatHubProxy != null)
            {
                 signalR.Disconnect();
            }

            LoadSignalRMessage();
        }
    }
}

참고URL : https://stackoverflow.com/questions/4783865/how-do-i-abort-cancel-tpl-tasks

반응형