Ты не можешь Задачи используют фоновые потоки из пула потоков. Также не рекомендуется отменять темы с помощью метода Abort. Вы можете взглянуть на следующий пост в блоге, который объясняет правильный способ отмены задач с использованием токенов отмены. Вот пример:
classProgram{staticvoidMain(){var ts =newCancellationTokenSource();CancellationToken ct = ts.Token;Task.Factory.StartNew(()=>{while(true){// do some heavy work hereThread.Sleep(100);if(ct.IsCancellationRequested){// another thread decided to cancelConsole.WriteLine("task canceled");break;}}}, ct);// Simulate waiting 3s for the task to completeThread.Sleep(3000);// Can't wait anymore => cancel this task
ts.Cancel();Console.ReadLine();}}
Хорошее объяснение. У меня есть вопрос, как это работает, когда у нас нет анонимного метода в Task.Factory.StartNew? как Task.Factory.StartNew (() => ProcessMyMethod (), cancellationToken)
Prerak K
61
Что делать, если есть блокирующий вызов, который не возвращается внутри выполняемой задачи?
mehmet6parmak
3
@ mehmet6parmak Я думаю, что единственное, что вы можете сделать, это использовать его, Task.Wait(TimeSpan / int)чтобы назначить (основанный на времени) крайний срок извне.
Марк
2
Что если у меня есть свой собственный класс для управления выполнением методов внутри нового Task? Что - то вроде: public int StartNewTask(Action method). Внутри StartNewTaskметода я создаю новый Taskпо: Task task = new Task(() => method()); task.Start();. Так как я могу управлять CancellationToken? Я также хотел бы знать, если Threadмне нужно реализовать логику, чтобы проверить, есть ли какие-то задачи, которые все еще висят, и поэтому убить их, когда Form.Closing. С Threadsя использую Thread.Abort().
Чеширский кот
Боже, какой плохой пример! Это простое логическое условие, конечно, это первое, что можно попробовать! Но у меня есть функция в отдельной Задаче, выполнение которой может занять много времени, и в идеале она не должна ничего знать о потоке или о чем-то еще. Так как я могу отменить функцию с вашим советом?
Привет-ангел
32
Прерывание задачи легко возможно, если вы захватываете поток, в котором выполняется задача. Вот пример кода, демонстрирующий это:
voidMain(){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 displayedConsole.WriteLine("Task finished!");});//This is needed in the example to avoid thread being still NULLThread.Sleep(10);//Cancel the task by aborting the thread
thread.Abort();}
Я использовал Task.Run (), чтобы показать наиболее распространенный вариант использования этого - использование удобства Tasks со старым однопоточным кодом, который не использует класс CancellationTokenSource, чтобы определить, следует ли его отменить или нет.
Спасибо за эту идею. Использовал этот подход для реализации тайм-аута для некоторого внешнего кода, который не CancellationTokenподдерживает ...
Кристоф Финк
7
AFAIK thread.abort оставит вас неизвестным о вашем стеке, он может быть недействительным. Я никогда не пробовал, но я предполагаю, что при запуске потока в отдельном домене приложения будет сохранен файл thread.abort! Кроме того, весь поток тратится впустую в вашем решении только для того, чтобы прервать одну задачу. Вам не придется использовать задачи, но потоки в первую очередь. (downvote)
Мартин Мизер
1
Как я уже писал, это решение является последним средством, которое может рассматриваться при определенных обстоятельствах. Конечно, CancellationTokenили даже более простые решения, которые не содержат условий гонки, должны быть рассмотрены. Приведенный выше код только иллюстрирует метод, а не область использования.
Флориан Раппл
8
Я думаю, что этот подход может иметь неизвестные последствия, и я бы не рекомендовал его в производственном коде. Не всегда гарантируется, что задачи выполняются в другом потоке, что означает, что они могут выполняться в том же потоке, что и тот, который их создал, если это решит планировщик (что будет означать, что основной поток будет передан threadлокальной переменной). В вашем коде вы можете в итоге прервать основной поток, а это не то, что вам действительно нужно. Возможно, было бы неплохо проверить, совпадают ли потоки перед прерыванием, если вы настаиваете на прерывании
Ивайло Славов
10
@Martin - Поскольку я исследовал этот вопрос на SO, я вижу, что вы отклонили несколько ответов, которые используют Thread.Abort для уничтожения задач, но вы не предоставили никаких альтернативных решений. Как можно убить сторонний код, который не поддерживает отмену и выполняется в задании ?
Гордон Бин
32
Как следует из этого поста , это можно сделать следующим образом:
intFoo(CancellationToken token){Thread t =Thread.CurrentThread;
using (token.Register(t.Abort)){// compute-bound work here}}
Хотя это работает, не рекомендуется использовать такой подход. Если вы можете контролировать код, который выполняется в задаче, вам лучше использовать правильную обработку отмены.
+1 за то, что дал другой подход, указав свои запасные варианты. Я не знал, что это можно сделать :)
Джоэл
1
Спасибо за решение! Мы можем просто передать token методу и отменить tokensource вместо того, чтобы каким-либо образом получить экземпляр потока из метода и напрямую прервать его.
Подобные вещи являются одной из материально-технических причин, почему Abortэто устарело. Прежде всего, не используйте Thread.Abort()для отмены или остановки потока, если это вообще возможно. Abort()следует использовать только для принудительного уничтожения потока, который не отвечает на более мирные запросы о своевременной остановке.
При этом вам необходимо предоставить общий индикатор отмены, который один поток устанавливает и ожидает, пока другой поток периодически проверяет и корректно завершает работу. .NET 4 включает структуру, разработанную специально для этой цели CancellationToken.
Вы не должны пытаться сделать это напрямую. Создайте свои задачи для работы с CancellationToken и отмените их таким образом.
Кроме того, я бы порекомендовал изменить основной поток, чтобы он функционировал через CancellationToken. Звонить Thread.Abort()- плохая идея - это может привести к различным проблемам, которые очень сложно диагностировать. Вместо этого, что поток может использовать один и тот же Аннулирование , что ваши задачи использовать - и то же самое CancellationTokenSourceможет быть использован , чтобы вызвать отмену всех ваших задач и ваш основной поток.
Это приведет к гораздо более простому и безопасному дизайну.
Чтобы ответить на вопрос Prerak K о том, как использовать CancellationTokens, когда не используется анонимный метод в Task.Factory.StartNew (), вы передаете CancellationToken в качестве параметра в метод, который вы начинаете с StartNew (), как показано в примере MSDN сюда .
например
var tokenSource =newCancellationTokenSource();var token = tokenSource.Token;Task.Factory.StartNew(()=>DoSomeWork(1, token), token);staticvoidDoSomeWork(int taskNum,CancellationToken ct){// Do work here, checking and acting on ct.IsCancellationRequested where applicable, }
Я использую смешанный подход, чтобы отменить задачу.
Во-первых, я пытаюсь отменить это вежливо с помощью отмены .
Если он все еще работает (например, из-за ошибки разработчика), то ведите себя плохо и убивайте его, используя метод Abort старой школы .
Проверьте пример ниже:
privateCancellationTokenSource taskToken;privateAutoResetEvent awaitReplyOnRequestEvent =newAutoResetEvent(false);voidMain(){// Start a task which is doing nothing but sleeps 1sLaunchTaskAsync();Thread.Sleep(100);// Stop the taskStopTask();}/// <summary>/// Launch task in a new thread/// </summary>voidLaunchTaskAsync(){
taskToken =newCancellationTokenSource();Task.Factory.StartNew(()=>{try{//Capture the thread
runningTaskThread =Thread.CurrentThread;// Run the taskif(taskToken.IsCancellationRequested||!awaitReplyOnRequestEvent.WaitOne(10000))return;Console.WriteLine("Task finished!");}catch(Exception exc){// Handle exception}}, taskToken.Token);}/// <summary>/// Stop running task/// </summary>voidStopTask(){// Attempt to cancel the task politelyif(taskToken !=null){if(taskToken.IsCancellationRequested)return;else
taskToken.Cancel();}// Notify a waiting thread that an event has occurredif(awaitReplyOnRequestEvent !=null)
awaitReplyOnRequestEvent.Set();// If 1 sec later the task is still running, kill it cruellyif(runningTaskThread !=null){try{
runningTaskThread.Join(TimeSpan.FromSeconds(1));}catch(Exception ex){
runningTaskThread.Abort();}}}
Вы можете использовать CancellationTokenдля контроля отмены задачи. Вы говорите о том, чтобы прервать его до его начала («неважно, я уже сделал это») или фактически прерываете его посередине? Если первое, то CancellationTokenможет быть полезно; в последнем случае вам, вероятно, потребуется реализовать свой собственный механизм «спасения» и проверить в соответствующих точках выполнения задачи, следует ли вам быстро потерпеть неудачу (вы все равно можете использовать CancellationToken, чтобы помочь вам, но это немного больше ручного управления).
Задачи выполняются в ThreadPool (по крайней мере, если вы используете фабрику по умолчанию), поэтому прерывание потока не может повлиять на задачи. Для отмены задач см. Отмена задач на msdn.
Я пытался, CancellationTokenSourceно я не могу этого сделать. И я сделал это по-своему. И это работает.
namespace Blokick.Provider{publicclassSignalRConnectProvider{publicSignalRConnectProvider(){}publicboolIsStopRequested{get;set;}=false;//1-)This is important and default `false`.publicasyncTask<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)]publicpartialclassMessagePerson:ContentPage{SignalRConnectProvider signalR =newSignalRConnectProvider();publicMessagePerson(){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создайте новый поток и используйте его для выполнения задачи. Позже вы можете прервать поток, что приведет к завершению задачи в ошибочном состоянии с помощью ThreadAbortException.
Thread.AbortМетод следует использовать с осторожностью. В частности, когда вы вызываете его для прерывания потока, отличного от текущего потока, вы не знаете, какой код был выполнен или не удалось выполнить, когда выдается исключение ThreadAbortException , а также вы не можете быть уверены в состоянии вашего приложения или любого приложения и пользовательского состояния. что он отвечает за сохранение. Например, вызов Thread.Abortможет препятствовать выполнению статических конструкторов или препятствовать освобождению неуправляемых ресурсов.
Этот код завершается с исключением во время выполнения: System.InvalidOperationException: RunSynchronously не может быть вызван для задачи, которая уже была запущена.
Теодор Зулиас
1
@ TheodorZoulias Хороший улов. Спасибо. Я исправил код и в целом улучшил ответ.
Эдвард Брей
1
Да, это исправило ошибку. Еще одна оговорка, о которой следует упомянуть, это то, что Thread.Abortона не поддерживается в .NET Core. Попытка его использования приводит к исключению: System.PlatformNotSupportedException: прерывание потока не поддерживается на этой платформе. Третье предостережение заключается в том, что SingleThreadTaskSchedulerего нельзя эффективно использовать с задачами в стиле обещания, другими словами, с задачами, созданными с asyncделегатами. Например, встроенное выполнение await Task.Delay(1000)выполняется без потока, поэтому оно не подвержено влиянию событий потока.
Ответы:
Ты не можешь Задачи используют фоновые потоки из пула потоков. Также не рекомендуется отменять темы с помощью метода Abort. Вы можете взглянуть на следующий пост в блоге, который объясняет правильный способ отмены задач с использованием токенов отмены. Вот пример:
источник
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()
.Прерывание задачи легко возможно, если вы захватываете поток, в котором выполняется задача. Вот пример кода, демонстрирующий это:
Я использовал Task.Run (), чтобы показать наиболее распространенный вариант использования этого - использование удобства Tasks со старым однопоточным кодом, который не использует класс CancellationTokenSource, чтобы определить, следует ли его отменить или нет.
источник
CancellationToken
поддерживает ...CancellationToken
или даже более простые решения, которые не содержат условий гонки, должны быть рассмотрены. Приведенный выше код только иллюстрирует метод, а не область использования.thread
локальной переменной). В вашем коде вы можете в итоге прервать основной поток, а это не то, что вам действительно нужно. Возможно, было бы неплохо проверить, совпадают ли потоки перед прерыванием, если вы настаиваете на прерыванииКак следует из этого поста , это можно сделать следующим образом:
Хотя это работает, не рекомендуется использовать такой подход. Если вы можете контролировать код, который выполняется в задаче, вам лучше использовать правильную обработку отмены.
источник
Подобные вещи являются одной из материально-технических причин, почему
Abort
это устарело. Прежде всего, не используйтеThread.Abort()
для отмены или остановки потока, если это вообще возможно.Abort()
следует использовать только для принудительного уничтожения потока, который не отвечает на более мирные запросы о своевременной остановке.При этом вам необходимо предоставить общий индикатор отмены, который один поток устанавливает и ожидает, пока другой поток периодически проверяет и корректно завершает работу. .NET 4 включает структуру, разработанную специально для этой цели
CancellationToken
.источник
Вы не должны пытаться сделать это напрямую. Создайте свои задачи для работы с CancellationToken и отмените их таким образом.
Кроме того, я бы порекомендовал изменить основной поток, чтобы он функционировал через CancellationToken. Звонить
Thread.Abort()
- плохая идея - это может привести к различным проблемам, которые очень сложно диагностировать. Вместо этого, что поток может использовать один и тот же Аннулирование , что ваши задачи использовать - и то же самоеCancellationTokenSource
может быть использован , чтобы вызвать отмену всех ваших задач и ваш основной поток.Это приведет к гораздо более простому и безопасному дизайну.
источник
Чтобы ответить на вопрос Prerak K о том, как использовать CancellationTokens, когда не используется анонимный метод в Task.Factory.StartNew (), вы передаете CancellationToken в качестве параметра в метод, который вы начинаете с StartNew (), как показано в примере MSDN сюда .
например
источник
Я использую смешанный подход, чтобы отменить задачу.
Проверьте пример ниже:
источник
Задачи имеют первоклассную поддержку отмены через токены отмены . Создайте свои задачи с токенами отмены, и отмените задачи через них явно.
источник
Вы можете использовать
CancellationToken
для контроля отмены задачи. Вы говорите о том, чтобы прервать его до его начала («неважно, я уже сделал это») или фактически прерываете его посередине? Если первое, тоCancellationToken
может быть полезно; в последнем случае вам, вероятно, потребуется реализовать свой собственный механизм «спасения» и проверить в соответствующих точках выполнения задачи, следует ли вам быстро потерпеть неудачу (вы все равно можете использовать CancellationToken, чтобы помочь вам, но это немного больше ручного управления).В MSDN есть статья об отмене задач: http://msdn.microsoft.com/en-us/library/dd997396.aspx
источник
Задачи выполняются в ThreadPool (по крайней мере, если вы используете фабрику по умолчанию), поэтому прерывание потока не может повлиять на задачи. Для отмены задач см. Отмена задач на msdn.
источник
Я пытался,
CancellationTokenSource
но я не могу этого сделать. И я сделал это по-своему. И это работает.И еще один класс вызова метода:
источник
Вы можете прервать задачу как поток, если можете заставить задачу быть созданной в ее собственном потоке и вызвать
Abort
ееThread
объект. По умолчанию задача выполняется в потоке пула потоков или в вызывающем потоке - ни один из которых вы обычно не хотите прерывать.Чтобы задача получила собственный поток, создайте собственный планировщик, полученный из
TaskScheduler
. В вашей реализацииQueueTask
создайте новый поток и используйте его для выполнения задачи. Позже вы можете прервать поток, что приведет к завершению задачи в ошибочном состоянии с помощьюThreadAbortException
.Используйте этот планировщик задач:
Начните свою задачу так:
Позже вы можете прервать с помощью:
Обратите внимание, что предупреждение об прерывании потока все еще применяется:
источник
Thread.Abort
она не поддерживается в .NET Core. Попытка его использования приводит к исключению: System.PlatformNotSupportedException: прерывание потока не поддерживается на этой платформе. Третье предостережение заключается в том, чтоSingleThreadTaskScheduler
его нельзя эффективно использовать с задачами в стиле обещания, другими словами, с задачами, созданными сasync
делегатами. Например, встроенное выполнениеawait Task.Delay(1000)
выполняется без потока, поэтому оно не подвержено влиянию событий потока.