Как прервать/отменить TPL Tasks?

В потоке я создаю несколько System.Threading.Task и запускаю каждую задачу.

Когда я делаю .Abort() для уничтожения потока, задачи не прерываются.

Как передать .Abort() мои задачи?

Ответ 1

Вы не можете. В задачах используются потоки фона из пула потоков. Также не рекомендуется отменять потоки с использованием метода 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();
    }
}

Ответ 2

Отмена задачи легко возможна, если вы фиксируете поток, в котором выполняется задача. Вот пример кода, чтобы продемонстрировать это:

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, чтобы определить, следует ли его отменять или нет.

Ответ 3

Как этот пост, это можно сделать следующим образом:

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

Хотя он работает, не рекомендуется использовать такой подход. Если вы можете контролировать код, выполняемый в задаче, лучше подойдите к правильной обработке отмены.

Ответ 4

Это одна из логистических причин, почему Abort устарел. В первую очередь не использовать Thread.Abort() для отмены или остановки потока, если это вообще возможно. Abort() следует использовать только для принудительного уничтожения потока, который не отвечает на более мирные запросы, чтобы остановить своевременно.

При этом вам нужно предоставить индикатор общей отмены, который один поток устанавливает и ждет, пока другой поток периодически проверяет и изящно завершает работу..NET 4 включает структуру, разработанную специально для этой цели, CancellationToken.

Ответ 5

Вы не должны пытаться делать это напрямую. Создайте свои задачи для работы с CancellationToken и отмените их таким образом.

Кроме того, я бы рекомендовал изменить основной поток для работы через CancellationToken. Вызов Thread.Abort() - плохая идея - это может привести к различным проблемам, которые очень трудно диагностировать. Вместо этого этот поток может использовать ту же Отмена, что ваши задачи используют - и тот же CancellationTokenSource может использоваться для запуска отмены всех ваших задач и основного потока.

Это приведет к гораздо более простому и безопасному дизайну.

Ответ 6

Чтобы ответить на вопрос Prerak K о том, как использовать CancellationTokens, когда вы не используете анонимный метод в Task.Factory.StartNew(), вы передаете CancellationToken в качестве параметра в метод, который вы начинаете с StartNew(), как показано в пример MSDN здесь.

например.

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, 

}

Ответ 7

Вы можете использовать CancellationToken для управления отменой задачи. Вы говорите об отмене его до его начала ( "неважно, я уже это сделал" ) или на самом деле прерывая его посередине? Если первая, CancellationToken может быть полезна; если последнее, вам, вероятно, понадобится реализовать свой собственный механизм "спасения" и проверить в соответствующих точках выполнения задачи, следует ли вам быстро работать (вы можете использовать CancellationToken, чтобы помочь вам, но это немного более ручной).

В MSDN есть статья об отмене задач: http://msdn.microsoft.com/en-us/library/dd997396.aspx

Ответ 8

Я использую смешанный подход для отмены задачи.

  • Во-первых, я стараюсь отменить его вежливо, используя Отмена.
  • Если он все еще запущен (например, из-за ошибки разработчика), то неправильно и убить его, используя метод

Ответ 9

Задача выполняется в ThreadPool (по крайней мере, если вы используете по умолчанию factory), поэтому прерывание потока не может повлиять на задачи. Для прерывания задач см. Отмена задачи в msdn.

Ответ 10

Задачи имеют поддержку первого класса для отмены через токены отмены. Создайте свои задачи с помощью токенов отмены и отмените задачи через них.

Ответ 11

Я пробовал CancellationTokenSource, но я не могу этого сделать. И я сделал это по-своему. И это работает.

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

И еще один класс вызова метода:

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