У нитці я створюю деякі System.Threading.Task
і запускаю кожне завдання.
Коли я роблю a, .Abort()
щоб вбити нитку, завдання не скасовуються.
Як я можу передати .Abort()
свої завдання?
У нитці я створюю деякі System.Threading.Task
і запускаю кожне завдання.
Коли я роблю a, .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();
}
}
Task.Wait(TimeSpan / int)
щоб дати йому (часовий) термін ззовні.
Task
? Що - щось на кшталт: public int StartNewTask(Action method)
. Усередині StartNewTask
методу я створюю новий Task
по: Task task = new Task(() => method()); task.Start();
. Тож як я можу керувати CancellationToken
? Я також хотів би знати, якщо Thread
мені потрібно застосувати логіку, щоб перевірити, чи є якісь завдання, які все ще висять, і тому вбивати їх, коли Form.Closing
. З Threads
використанням Thread.Abort()
.
Відмова від завдання легко можлива, якщо ви захопите потік, у якому виконується завдання. Ось приклад коду для демонстрації цього:
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, щоб визначити, чи слід його скасувати чи ні.
CancellationToken
підтримує ...
CancellationToken
слід враховувати або навіть більш прості рішення, які не належать до перегонів. Код, наведений вище, ілюструє лише метод, а не область використання.
thread
локальної змінної). Тоді у вашому коді ви можете перервати основну нитку, а це не те, чого ви дійсно хочете. Можливо, перевірити, чи нитки є однаковими перед абортом, було б добре подумати, якщо ви наполягаєте на перериванні вагітності
Як передбачає ця публікація , це можна зробити наступним чином:
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,
}
Я використовую змішаний підхід для скасування завдання.
Отримайте приклад нижче:
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();
}
}
}
У завданнях є підтримка першого класу для скасування через маркери скасування . Створіть свої завдання з маркерів для скасування та скасуйте ці завдання безпосередньо.
Ви можете використовувати a, CancellationToken
щоб контролювати, чи завдання скасовується. Ви говорите про переривання його перед початком роботи ("ніколи, я вже це робив"), чи фактично перериваєте його посередині? Якщо перший, то CancellationToken
може бути корисним; якщо останнє, вам, ймовірно, потрібно буде впровадити власний механізм "викупу" і перевірити у відповідних точках виконання завдання, чи не слід швидко виходити з ладу (ви все одно можете скористатися CancellationToken, щоб допомогти вам, але це трохи більше інструкції).
У MSDN є стаття про скасування завдань: http://msdn.microsoft.com/en-us/library/dd997396.aspx
Завдання виконуються на ThreadPool (принаймні, якщо ви використовуєте заводські налаштування за замовчуванням), тому переривання потоку не може вплинути на завдання. Про припинення завдань див. У розділі Скасування завдань на 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();
}
}
}
Ви можете перервати завдання, як нитка, якщо ви зможете створити завдання на власній потоці та викликати Abort
його Thread
об’єкт. За замовчуванням завдання виконується на потоці пулу потоків або викличній нитці - жоден з яких ви зазвичай не хочете припинити.
Щоб завдання отримало власну нитку, створіть призначений для користувача планувальник TaskScheduler
. У вашому виконанні QueueTask
створіть новий потік і використовуйте його для виконання завдання. Пізніше ви можете перервати нитку, що призведе до завершення завдання у несправному стані з a ThreadAbortException
.
Використовуйте цей планувальник завдань:
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
}
Почніть своє завдання так:
var scheduler = new SingleThreadTaskScheduler();
var task = Task.Factory.StartNew(action, cancellationToken, TaskCreationOptions.LongRunning, scheduler);
Пізніше ви можете перервати:
scheduler.TaskThread.Abort();
Зауважте, що застереження щодо переривання потоку все ще застосовується:
Thread.Abort
Метод слід використовувати з обережністю. Зокрема, коли ви закликаєте його перервати інший потік, ніж поточний потік, ви не знаєте, який код виконано чи не вдалося виконати, коли ThreadAbortException кинуто, і ви не можете бути впевнені в стані вашої програми або будь-якому додатку та стані користувача що він відповідає за збереження. Наприклад, викликThread.Abort
може запобігти виконанню статичних конструкторів або запобігти випуску некерованих ресурсів.
Thread.Abort
він не підтримується у .NET Core. Спроба використовувати його призводить до виключення: System.PlatformNotSupportedException: Переривання теми не підтримується на цій платформі. Третє застереження полягає в тому, що SingleThreadTaskScheduler
не можна ефективно використовувати завдання із стилем обіцянок, іншими словами, із завданнями, створеними з async
делегатами. Наприклад, вбудована програма не await Task.Delay(1000)
працює в потоці, тому це не впливає на події потоку.