В потоке я создаю несколько System.Threading.Task
и запускаю каждую задачу.
Когда я делаю .Abort()
для уничтожения потока, задачи не прерываются.
Как передать .Abort()
мои задачи?
В потоке я создаю несколько 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
может использоваться для запуска отмены всех ваших задач и основного потока.
Это приведет к гораздо более простому и безопасному дизайну.
Чтобы ответить на вопрос 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,
}
Вы можете использовать CancellationToken
для управления отменой задачи. Вы говорите об отмене его до его начала ( "неважно, я уже это сделал" ) или на самом деле прерывая его посередине? Если первая, CancellationToken
может быть полезна; если последнее, вам, вероятно, понадобится реализовать свой собственный механизм "спасения" и проверить в соответствующих точках выполнения задачи, следует ли вам быстро работать (вы можете использовать CancellationToken, чтобы помочь вам, но это немного более ручной).
В MSDN есть статья об отмене задач: http://msdn.microsoft.com/en-us/library/dd997396.aspx
Я использую смешанный подход для отмены задачи.
Задача выполняется в ThreadPool (по крайней мере, если вы используете по умолчанию factory), поэтому прерывание потока не может повлиять на задачи. Для прерывания задач см. Отмена задачи в msdn.
Задачи имеют поддержку первого класса для отмены через токены отмены. Создайте свои задачи с помощью токенов отмены и отмените задачи через них.
Я пробовал 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();
}
}
}