How do I abort/cancel TPL Tasks?

前端 未结 12 2082
佛祖请我去吃肉
佛祖请我去吃肉 2020-11-22 11:42

In a thread, I create some System.Threading.Task and start each task.

When I do a .Abort() to kill the thread, the tasks are not aborted.

相关标签:
12条回答
  • 2020-11-22 11:55

    Aborting a Task is easily possible if you capture the thread in which the task is running in. Here is an example code to demonstrate this:

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

    I used Task.Run() to show the most common use-case for this - using the comfort of Tasks with old single-threaded code, which does not use the CancellationTokenSource class to determine if it should be canceled or not.

    0 讨论(0)
  • 2020-11-22 11:56

    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();
            }
        }
    }
    
    0 讨论(0)
  • 2020-11-22 11:58

    To answer Prerak K's question about how to use CancellationTokens when not using an anonymous method in Task.Factory.StartNew(), you pass the CancellationToken as a parameter into the method you're starting with StartNew(), as shown in the MSDN example here.

    e.g.

    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, 
    
    }
    
    0 讨论(0)
  • 2020-11-22 11:59

    I use a mixed approach to cancel a task.

    • Firstly, I'm trying to Cancel it politely with using the Cancellation.
    • If it's still running (e.g. due to a developer's mistake), then misbehave and kill it using an old-school Abort method.

    Checkout an example below:

    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();
            }
        }
    }
    
    0 讨论(0)
  • You can abort a task like a thread if you can cause the task to be created on its own thread and call Abort on its Thread object. By default, a task runs on a thread pool thread or the calling thread - neither of which you typically want to abort.

    To ensure the task gets its own thread, create a custom scheduler derived from TaskScheduler. In your implementation of QueueTask, create a new thread and use it to execute the task. Later, you can abort the thread, which will cause the task to complete in a faulted state with a ThreadAbortException.

    Use this task scheduler:

    class SingleThreadTaskScheduler : TaskScheduler
    {
        public Thread TaskThread { get; private set; }
    
        protected override void QueueTask(Task task)
        {
            TaskThread = new Thread(() => TryExecuteTask(task));
            TaskThread.Start();
        }
    
        protected override IEnumerable<Task> GetScheduledTasks() => throw new NotSupportedException(); // Unused
        protected override bool NotSupportedException(Task task, bool taskWasPreviouslyQueued) => throw new NotSupportedException(); // Unused
    }
    

    Start your task like this:

    var scheduler = new SingleThreadTaskScheduler();
    var task = Task.Factory.StartNew(action, cancellationToken, TaskCreationOptions.LongRunning, scheduler);
    

    Later, you can abort with:

    scheduler.TaskThread.Abort();
    

    Note that the caveat about aborting a thread still applies:

    The Thread.Abort method should be used with caution. Particularly when you call it to abort a thread other than the current thread, you do not know what code has executed or failed to execute when the ThreadAbortException is thrown, nor can you be certain of the state of your application or any application and user state that it is responsible for preserving. For example, calling Thread.Abort may prevent static constructors from executing or prevent the release of unmanaged resources.

    0 讨论(0)
  • 2020-11-22 12:05

    This sort of thing is one of the logistical reasons why Abort is deprecated. First and foremost, do not use Thread.Abort() to cancel or stop a thread if at all possible. Abort() should only be used to forcefully kill a thread that is not responding to more peaceful requests to stop in a timely fashion.

    That being said, you need to provide a shared cancellation indicator that one thread sets and waits while the other thread periodically checks and gracefully exits. .NET 4 includes a structure designed specifically for this purpose, the CancellationToken.

    0 讨论(0)
提交回复
热议问题