In un thread, ne creo alcuni System.Threading.Task
e inizio ogni attività.
Quando faccio un .Abort()
per uccidere il thread, le attività non vengono interrotte.
Come posso trasmetterlo .Abort()
ai miei compiti?
In un thread, ne creo alcuni System.Threading.Task
e inizio ogni attività.
Quando faccio un .Abort()
per uccidere il thread, le attività non vengono interrotte.
Come posso trasmetterlo .Abort()
ai miei compiti?
Risposte:
Non puoi. Le attività utilizzano thread in background dal pool di thread. Si sconsiglia inoltre di annullare le discussioni utilizzando il metodo Abort. Puoi dare un'occhiata al seguente post sul blog che spiega un modo corretto di annullare le attività utilizzando i token di cancellazione. Ecco un esempio:
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)
per dargli una scadenza (basata sul tempo) dall'esterno.
Task
? Qualcosa come: public int StartNewTask(Action method)
. All'interno del StartNewTask
metodo creo un nuovo Task
da: Task task = new Task(() => method()); task.Start();
. Quindi, come posso gestire CancellationToken
? Vorrei anche sapere se Thread
ho bisogno di implementare una logica per verificare se ci sono alcune attività che sono ancora in sospeso e quindi ucciderle quando Form.Closing
. Con Threads
uso Thread.Abort()
.
L'annullamento di un'attività è facilmente possibile se si acquisisce il thread in cui è in esecuzione l'attività. Ecco un codice di esempio per dimostrarlo:
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();
}
Ho usato Task.Run () per mostrare il caso d'uso più comune per questo - usando il comfort di Tasks con il vecchio codice a thread singolo, che non usa la classe CancelTokenSource per determinare se debba essere annullato o meno.
CancellationToken
supporto ...
CancellationToken
dovrebbe prendere in considerazione una o anche soluzioni più semplici che siano prive di condizioni di gara. Il codice sopra illustra solo il metodo, non l'area di utilizzo.
thread
variabile locale). Nel tuo codice potresti finire per interrompere il thread principale, che non è quello che vuoi davvero. Forse un controllo se i fili sono gli stessi prima che l'interruzione sarebbe una buona idea, se insisti ad abortire
Come suggerisce questo post , questo può essere fatto nel modo seguente:
int Foo(CancellationToken token)
{
Thread t = Thread.CurrentThread;
using (token.Register(t.Abort))
{
// compute-bound work here
}
}
Sebbene funzioni, non è consigliabile utilizzare tale approccio. Se riesci a controllare il codice che viene eseguito nell'attività, è meglio andare con la corretta gestione della cancellazione.
Questo genere di cose è uno dei motivi logistici per cui Abort
è deprecato. Innanzitutto, non utilizzare Thread.Abort()
per annullare o interrompere una discussione, se possibile. Abort()
dovrebbe essere usato solo per uccidere forzatamente un thread che non risponde a richieste più pacifiche per fermarsi in modo tempestivo.
Detto questo, è necessario fornire un indicatore di annullamento condiviso che un thread imposta e attende mentre l'altro thread controlla periodicamente ed esce con grazia. .NET 4 include una struttura progettata appositamente per questo scopo, il CancellationToken
.
Non dovresti provare a farlo direttamente. Progetta le tue attività in modo che funzionino con una cancellazione annullata e annullale in questo modo.
Inoltre, consiglierei di modificare il thread principale affinché funzioni anche tramite un CancelToken. Chiamare Thread.Abort()
è una cattiva idea: può portare a vari problemi che sono molto difficili da diagnosticare. Al contrario, quel thread può utilizzare la stessa Annullamento utilizzato dalle attività dell'utente e lo stesso CancellationTokenSource
può essere utilizzato per attivare l'annullamento di tutte le attività e il thread principale.
Ciò porterà a un design molto più semplice e sicuro.
Per rispondere alla domanda di Prerak K su come utilizzare CancelTokens quando non si utilizza un metodo anonimo in Task.Factory.StartNew (), si passa a CancelToken come parametro nel metodo che si inizia con StartNew (), come mostrato nell'esempio MSDN qUI .
per esempio
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,
}
Uso un approccio misto per annullare un'attività.
Guarda un esempio di seguito:
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();
}
}
}
Le attività hanno un supporto di prima classe per la cancellazione tramite token di cancellazione . Crea le tue attività con i token di annullamento e annulla le attività in modo esplicito.
È possibile utilizzare a CancellationToken
per controllare se l'attività viene annullata. Stai parlando di interromperlo prima che inizi ("non importa, l'ho già fatto") o di interromperlo nel mezzo? Se il primo, il CancellationToken
può essere utile; in quest'ultimo caso, sarà probabilmente necessario implementare il proprio meccanismo di "salvataggio" e verificare nei punti appropriati dell'esecuzione dell'attività se si dovrebbe fallire rapidamente (è comunque possibile utilizzare il comando CancelToken per aiutarti, ma è un po 'più manuale).
MSDN ha un articolo sull'annullamento delle attività: http://msdn.microsoft.com/en-us/library/dd997396.aspx
Le attività vengono eseguite sul ThreadPool (almeno, se si utilizza la factory predefinita), quindi l'interruzione del thread non può influire sulle attività. Per interrompere le attività, vedere Annullamento attività su msdn.
Ci ho provato CancellationTokenSource
ma non posso farlo. E l'ho fatto a modo mio. E funziona
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 un'altra classe di chiamare il metodo:
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();
}
}
}
È possibile interrompere un'attività come un thread se è possibile che l'attività venga creata sul proprio thread e che venga richiamato Abort
sul suo Thread
oggetto. Per impostazione predefinita, un'attività viene eseguita su un thread del pool di thread o sul thread chiamante, nessuno dei quali in genere si desidera interrompere.
Per assicurarsi che l'attività ottenga il proprio thread, creare uno scheduler personalizzato derivato TaskScheduler
. Nella tua implementazione di QueueTask
, crea un nuovo thread e usalo per eseguire l'attività. Successivamente, è possibile interrompere il thread, che comporterà il completamento dell'attività in uno stato di errore con a ThreadAbortException
.
Utilizzare questo Utilità di pianificazione:
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
}
Inizia l'attività in questo modo:
var scheduler = new SingleThreadTaskScheduler();
var task = Task.Factory.StartNew(action, cancellationToken, TaskCreationOptions.LongRunning, scheduler);
Successivamente, puoi interrompere con:
scheduler.TaskThread.Abort();
Si noti che l' avvertenza sull'interruzione di un thread rimane valida:
Il
Thread.Abort
metodo deve essere usato con cautela. Soprattutto quando lo chiami per interrompere un thread diverso dal thread corrente, non sai quale codice è stato eseguito o non è stato eseguito quando viene lanciata ThreadAbortException , né puoi essere certo dello stato della tua applicazione o di qualsiasi stato dell'applicazione e utente che è responsabile per la conservazione. Ad esempio, la chiamataThread.Abort
può impedire l'esecuzione di costruttori statici o il rilascio di risorse non gestite.
Thread.Abort
non è supportato su .NET Core. Tentare di usarlo comporta un'eccezione: System.PlatformNotSupportedException: l'interruzione del thread non è supportata su questa piattaforma. Un terzo avvertimento è che SingleThreadTaskScheduler
non può essere utilizzato in modo efficace con attività di tipo promettente, in altre parole con attività create con i async
delegati. Ad esempio, un incorporato await Task.Delay(1000)
viene eseguito senza thread, quindi non è interessato dagli eventi thread.