In einem Thread erstelle ich einige System.Threading.Task
und starte jede Aufgabe.
Wenn ich a mache .Abort()
, um den Thread zu beenden, werden die Aufgaben nicht abgebrochen.
Wie kann ich das .Abort()
auf meine Aufgaben übertragen?
In einem Thread erstelle ich einige System.Threading.Task
und starte jede Aufgabe.
Wenn ich a mache .Abort()
, um den Thread zu beenden, werden die Aufgaben nicht abgebrochen.
Wie kann ich das .Abort()
auf meine Aufgaben übertragen?
Antworten:
Das kannst du nicht. Aufgaben verwenden Hintergrundthreads aus dem Threadpool. Das Abbrechen von Threads mit der Abort-Methode wird ebenfalls nicht empfohlen. Sie können sich den folgenden Blog-Beitrag ansehen, in dem erläutert wird, wie Aufgaben mithilfe von Stornierungs-Token ordnungsgemäß abgebrochen werden. Hier ist ein Beispiel:
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)
, ihm eine (zeitbasierte) Frist von außen zu geben.
Task
? So etwas wie : public int StartNewTask(Action method)
. Innerhalb der StartNewTask
Methode erstelle ich eine neue Task
von : Task task = new Task(() => method()); task.Start();
. Wie kann ich das verwalten CancellationToken
? Ich würde auch gerne wissen, ob Thread
ich eine Logik implementieren muss, um zu überprüfen, ob noch einige Aufgaben hängen, und sie dann zu töten, wenn Form.Closing
. Mit Threads
ich benutze Thread.Abort()
.
Das Abbrechen einer Aufgabe ist problemlos möglich, wenn Sie den Thread erfassen, in dem die Aufgabe ausgeführt wird. Hier ein Beispielcode, um dies zu demonstrieren:
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();
}
Ich habe Task.Run () verwendet, um den häufigsten Anwendungsfall dafür anzuzeigen - unter Verwendung des Komforts von Aufgaben mit altem Single-Threaded-Code, bei dem die CancellationTokenSource-Klasse nicht verwendet wird, um zu bestimmen, ob sie abgebrochen werden soll oder nicht.
CancellationToken
Unterstützung hat ...
CancellationToken
oder noch einfachere Lösungen in Betracht gezogen werden, die frei von Rennbedingungen sind. Der obige Code veranschaulicht nur die Methode, nicht den Anwendungsbereich.
thread
lokale Variable übergeben wird). In Ihrem Code wird der Hauptthread möglicherweise abgebrochen, was nicht wirklich gewünscht ist. Vielleicht wäre eine Überprüfung, ob die Threads vor dem Abbruch gleich sind, eine gute Idee, wenn Sie darauf bestehen, abzubrechen
Wie in diesem Beitrag vorgeschlagen , kann dies folgendermaßen erfolgen:
int Foo(CancellationToken token)
{
Thread t = Thread.CurrentThread;
using (token.Register(t.Abort))
{
// compute-bound work here
}
}
Obwohl es funktioniert, wird nicht empfohlen, einen solchen Ansatz zu verwenden. Wenn Sie den Code steuern können, der in der Aufgabe ausgeführt wird, sollten Sie die Stornierung ordnungsgemäß behandeln.
Diese Art von Dingen ist einer der logistischen Gründe, warum sie Abort
veraltet sind. Verwenden Sie Thread.Abort()
in erster Linie nicht , um einen Thread abzubrechen oder zu stoppen, wenn dies überhaupt möglich ist. Abort()
sollte nur verwendet werden, um einen Thread gewaltsam zu beenden, der nicht auf friedlichere Anfragen reagiert, rechtzeitig anzuhalten.
Davon abgesehen müssen Sie einen gemeinsamen Abbruchindikator bereitstellen, den ein Thread setzt und wartet, während der andere Thread regelmäßig prüft und ordnungsgemäß beendet. .NET 4 enthält eine speziell für diesen Zweck entwickelte Struktur, die CancellationToken
.
Sie sollten dies nicht direkt versuchen. Entwerfen Sie Ihre Aufgaben so, dass sie mit einem CancellationToken arbeiten , und brechen Sie sie auf diese Weise ab.
Außerdem würde ich empfehlen, Ihren Hauptthread so zu ändern, dass er auch über ein CancellationToken funktioniert. Anrufen Thread.Abort()
ist eine schlechte Idee - es kann zu verschiedenen Problemen führen, die sehr schwer zu diagnostizieren sind. Stattdessen kann dieser Thread dieselbe Stornierung verwenden , die Ihre Aufgaben verwenden - und dieselbe CancellationTokenSource
kann verwendet werden, um die Stornierung aller Ihrer Aufgaben und Ihres Hauptthreads auszulösen .
Dies führt zu einem weitaus einfacheren und sichereren Design.
Um die Frage von Prerak K zur Verwendung von CancellationTokens zu beantworten, wenn in Task.Factory.StartNew () keine anonyme Methode verwendet wird, übergeben Sie das CancellationToken als Parameter an die Methode, die Sie mit StartNew () beginnen, wie im MSDN-Beispiel gezeigt Hier .
z.B
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,
}
Ich benutze einen gemischten Ansatz, um eine Aufgabe abzubrechen.
Kasse ein Beispiel unten:
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();
}
}
}
Aufgaben bieten erstklassige Unterstützung für die Stornierung über Stornierungs-Token . Erstellen Sie Ihre Aufgaben mit Abbruchtoken und brechen Sie die Aufgaben über diese explizit ab.
Mit a können Sie CancellationToken
steuern, ob die Aufgabe abgebrochen wird. Sprechen Sie darüber, es abzubrechen, bevor es gestartet wird ("egal, ich habe das bereits getan") oder es tatsächlich in der Mitte zu unterbrechen? Wenn erstere, CancellationToken
kann das hilfreich sein; In letzterem Fall müssen Sie wahrscheinlich Ihren eigenen "Bail-out" -Mechanismus implementieren und an geeigneten Stellen in der Aufgabenausführung prüfen, ob Sie schnell fehlschlagen sollten (Sie können das CancellationToken weiterhin als Hilfe verwenden, es ist jedoch etwas manueller).
MSDN hat einen Artikel zum Abbrechen von Aufgaben: http://msdn.microsoft.com/en-us/library/dd997396.aspx
Aufgaben werden auf dem ThreadPool ausgeführt (zumindest wenn Sie die Standardfactory verwenden), sodass das Abbrechen des Threads keine Auswirkungen auf die Aufgaben haben kann. Für Aufgaben Abbruch findet Aufgabenabbruch auf msdn.
Ich habe es versucht, CancellationTokenSource
aber ich kann das nicht tun. Und das habe ich auf meine eigene Weise gemacht. Und es funktioniert.
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;
}
}
}
Und eine andere Klasse des Aufrufs der Methode:
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();
}
}
}
Sie können eine Aufgabe wie ein roter Faden abbrechen , wenn die Aufgabe auf einem eigenen Thread erstellt dazu führen kann , und rufen Abort
auf ihrenThread
Objekt . Standardmäßig wird eine Aufgabe in einem Thread-Pool-Thread oder dem aufrufenden Thread ausgeführt, von denen Sie normalerweise keinen abbrechen möchten.
Erstellen Sie einen benutzerdefinierten Scheduler, von dem abgeleitet wird, um sicherzustellen, dass die Aufgabe einen eigenen Thread erhält TaskScheduler
. QueueTask
Erstellen Sie in Ihrer Implementierung von einen neuen Thread und verwenden Sie ihn zum Ausführen der Aufgabe. Später können Sie den Thread abbrechen, wodurch die Aufgabe in einem fehlerhaften Zustand mit a abgeschlossen wird ThreadAbortException
.
Verwenden Sie diesen Taskplaner:
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
}
Beginnen Sie Ihre Aufgabe wie folgt:
var scheduler = new SingleThreadTaskScheduler();
var task = Task.Factory.StartNew(action, cancellationToken, TaskCreationOptions.LongRunning, scheduler);
Später können Sie abbrechen mit:
scheduler.TaskThread.Abort();
Beachten Sie, dass die Einschränkung zum Abbrechen eines Threads weiterhin gilt:
Die
Thread.Abort
Methode sollte mit Vorsicht angewendet werden. Insbesondere wenn Sie es aufrufen, um einen anderen Thread als den aktuellen Thread abzubrechen, wissen Sie nicht, welcher Code ausgeführt wurde oder nicht ausgeführt werden konnte, wenn die ThreadAbortException ausgelöst wird, und Sie können sich auch nicht über den Status Ihrer Anwendung oder einen Anwendungs- und Benutzerstatus sicher sein dass es für die Erhaltung verantwortlich ist. Beispielsweise kann ein AufrufThread.Abort
die Ausführung statischer Konstruktoren verhindern oder die Freigabe nicht verwalteter Ressourcen verhindern.
Thread.Abort
sie unter .NET Core nicht unterstützt wird. Der Versuch, es dort zu verwenden, führt zu einer Ausnahme: System.PlatformNotSupportedException: Thread-Abbruch wird auf dieser Plattform nicht unterstützt. Eine dritte Einschränkung ist, dass das SingleThreadTaskScheduler
nicht effektiv mit Aufgaben im Versprechungsstil verwendet werden kann, dh mit Aufgaben, die mit async
Delegierten erstellt wurden. Beispielsweise wird ein Embedded await Task.Delay(1000)
in keinem Thread ausgeführt, sodass Thread-Ereignisse davon nicht betroffen sind.