Em um thread, eu crio alguns System.Threading.Task
e inicio cada tarefa.
Quando eu faço um .Abort()
para matar o thread, as tarefas não são abortadas.
Como posso transmitir as .Abort()
minhas tarefas?
Em um thread, eu crio alguns System.Threading.Task
e inicio cada tarefa.
Quando eu faço um .Abort()
para matar o thread, as tarefas não são abortadas.
Como posso transmitir as .Abort()
minhas tarefas?
Respostas:
Você não pode. As tarefas usam threads de segundo plano do pool de threads. Também não é recomendável cancelar threads usando o método Abort. Você pode dar uma olhada na seguinte postagem no blog, que explica uma maneira adequada de cancelar tarefas usando tokens de cancelamento. Aqui está um exemplo:
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)
um prazo (com base no tempo) do lado de fora.
Task
? Algo como: public int StartNewTask(Action method)
. Dentro do StartNewTask
método que eu criar uma nova Task
por: Task task = new Task(() => method()); task.Start();
. Então, como posso gerenciar o CancellationToken
? Eu também gostaria de saber se Thread
preciso implementar uma lógica para verificar se ainda existem algumas tarefas pendentes e, portanto, matá-las quando Form.Closing
. Com Threads
eu uso Thread.Abort()
.
A interrupção de uma tarefa é facilmente possível se você capturar o encadeamento no qual a tarefa está sendo executada. Aqui está um código de exemplo para demonstrar isso:
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();
}
Usei Task.Run () para mostrar o caso de uso mais comum para isso - usando o conforto do Tasks com código de thread único antigo, que não usa a classe CancellationTokenSource para determinar se deve ou não ser cancelado.
CancellationToken
apoio ...
CancellationToken
ou mais simples soluções que são livres de condições de corrida devem ser consideradas. O código acima ilustra apenas o método, não a área de uso.
thread
variável local). No seu código, você pode acabar cancelando o thread principal, que não é o que você realmente deseja. Talvez uma verificação se os tópicos são os mesmos antes do aborto seria uma boa penso ter, se você insistir em abortar
Como este post sugere, isso pode ser feito da seguinte maneira:
int Foo(CancellationToken token)
{
Thread t = Thread.CurrentThread;
using (token.Register(t.Abort))
{
// compute-bound work here
}
}
Embora funcione, não é recomendável usar essa abordagem. Se você pode controlar o código que é executado na tarefa, é melhor seguir o tratamento adequado do cancelamento.
Esse tipo de coisa é uma das razões logísticas para a Abort
descontinuação. Em primeiro lugar, não use Thread.Abort()
para cancelar ou interromper um encadeamento, se possível. Abort()
deve ser usado apenas para eliminar forçosamente um encadeamento que não está respondendo a solicitações mais pacíficas para parar em tempo hábil.
Dito isto, é necessário fornecer um indicador de cancelamento compartilhado de que um segmento define e aguarda enquanto o outro segmento verifica e sai periodicamente normalmente. O .NET 4 inclui uma estrutura projetada especificamente para esse fim, o CancellationToken
.
Você não deve tentar fazer isso diretamente. Projete suas tarefas para trabalhar com um CancellationToken e cancele-as dessa maneira.
Além disso, eu recomendaria alterar o thread principal para funcionar também por meio de um CancellationToken. Ligar Thread.Abort()
é uma má ideia - pode levar a vários problemas que são muito difíceis de diagnosticar. Em vez disso, esse encadeamento pode usar o mesmo cancelamento usado pelas suas tarefas - e o mesmo CancellationTokenSource
pode ser usado para acionar o cancelamento de todas as suas tarefas e do encadeamento principal.
Isso levará a um design muito mais simples e seguro.
Para responder à pergunta de Prerak K sobre como usar CancellationTokens quando não estiver usando um método anônimo em Task.Factory.StartNew (), você passa o CancellationToken como um parâmetro para o método que você está iniciando com StartNew (), como mostra o exemplo do MSDN aqui .
por exemplo
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,
}
Eu uso uma abordagem mista para cancelar uma tarefa.
Confira um exemplo abaixo:
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();
}
}
}
As tarefas têm suporte de primeira classe para cancelamento por meio de tokens de cancelamento . Crie suas tarefas com tokens de cancelamento e cancele-as explicitamente.
Você pode usar a CancellationToken
para controlar se a tarefa é cancelada. Você está falando sobre abortá-lo antes de começar ("deixa pra lá, eu já fiz isso") ou realmente interrompe-o no meio? Se o primeiro, o CancellationToken
pode ser útil; neste último caso, você provavelmente precisará implementar seu próprio mecanismo de "resgate" e verificar nos pontos apropriados na execução da tarefa se deve falhar rapidamente (ainda é possível usar o CancellationToken para ajudá-lo, mas é um pouco mais manual).
O MSDN possui um artigo sobre o cancelamento de tarefas: http://msdn.microsoft.com/en-us/library/dd997396.aspx
As tarefas estão sendo executadas no ThreadPool (pelo menos, se você estiver usando o padrão de fábrica), portanto, o cancelamento do encadeamento não pode afetar as tarefas. Para interromper tarefas, consulte Cancelamento de tarefas no msdn.
Eu tentei, CancellationTokenSource
mas não posso fazer isso. E eu fiz isso do meu jeito. E isso funciona.
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;
}
}
}
E outra classe de chamar o método:
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();
}
}
}
Você pode abortar uma tarefa como um encadeamento se puder fazer com que a tarefa seja criada em seu próprio encadeamento e chamar Abort
seuThread
objeto. Por padrão, uma tarefa é executada em um thread do conjunto de encadeamentos ou no encadeamento de chamada - nenhum dos quais você normalmente deseja anular.
Para garantir que a tarefa obtenha seu próprio encadeamento, crie um planejador personalizado derivado TaskScheduler
. Na sua implementação de QueueTask
, crie um novo encadeamento e use-o para executar a tarefa. Posteriormente, você pode interromper o encadeamento, o que fará com que a tarefa seja concluída em um estado com falha com a ThreadAbortException
.
Use este planejador de tarefas:
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
}
Inicie sua tarefa assim:
var scheduler = new SingleThreadTaskScheduler();
var task = Task.Factory.StartNew(action, cancellationToken, TaskCreationOptions.LongRunning, scheduler);
Mais tarde, você pode abortar com:
scheduler.TaskThread.Abort();
Observe que a advertência sobre o cancelamento de um encadeamento ainda se aplica:
O
Thread.Abort
método deve ser usado com cautela. Particularmente quando você o chama para abortar um segmento que não seja o segmento atual, você não sabe qual código foi executado ou falhou ao ser executado quando o ThreadAbortException é acionado, nem pode ter certeza do estado do seu aplicativo ou de qualquer aplicativo e estado do usuário que é responsável pela preservação. Por exemplo, a chamadaThread.Abort
pode impedir a execução de construtores estáticos ou a liberação de recursos não gerenciados.
Thread.Abort
não há suporte no .NET Core. A tentativa de usá-lo resulta em uma exceção: System.PlatformNotSupportedException: O cancelamento de encadeamento não é suportado nesta plataforma. Uma terceira ressalva é que ela SingleThreadTaskScheduler
não pode ser usada efetivamente com tarefas no estilo de promessa, ou seja, com tarefas criadas com async
delegados. Por exemplo, um incorporado await Task.Delay(1000)
é executado em nenhum encadeamento, portanto, não é afetado por eventos de encadeamento.