it-swarm.com.de

Wie bringe ich TPL-Aufgaben ab?

In einem Thread erstelle ich System.Threading.Task und starte jede Aufgabe.

Wenn ich eine .Abort() mache, um den Thread zu beenden, werden die Aufgaben nicht abgebrochen.

Wie kann ich die .Abort() an meine Aufgaben übergeben?

136

Du kannst nicht Aufgaben verwenden Hintergrundthreads aus dem Threadpool. Das Abbrechen von Threads mit der Abort-Methode wird ebenfalls nicht empfohlen. Sie können einen Blick auf den folgenden Blogbeitrag werfen, in dem erläutert wird, wie Aufgaben mithilfe von Storno-Tokens storniert werden können. 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();
    }
}
202
Darin Dimitrov

Das Abbrechen einer Aufgabe ist leicht 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 darzustellen - mit dem Komfort von Aufgaben mit altem Single-Threaded-Code, der nicht mithilfe der CancellationTokenSource-Klasse bestimmt, ob er abgebrochen werden soll oder nicht.

27
Florian Rappl

Wie dieser Beitrag schlägt vor, kann dies auf folgende Weise geschehen:

int Foo(CancellationToken token)
{
    Thread t = Thread.CurrentThread;
    using (token.Register(t.Abort))
    {
        // compute-bound work here
    }
}

Obwohl es funktioniert, wird die Verwendung eines solchen Ansatzes nicht empfohlen. Wenn Sie den Code steuern können, der in der Task ausgeführt wird, sollten Sie besser mit der ordnungsgemäßen Verarbeitung von Stornierungen beginnen.

25
starteleport

Dies ist einer der logistischen Gründe, warum Abort veraltet ist. In erster Linie verwenden Thread.Abort() nicht, um einen Thread abzubrechen oder zu stoppen, wenn dies überhaupt möglich ist.Abort() sollte nur verwendet werden, um einen Thread mit Gewalt zu beenden, der nicht auf friedlichere Anforderungen reagiert, um rechtzeitig anzuhalten.

Sie müssen jedoch ein gemeinsames Stornierungskennzeichen angeben, das ein Thread setzt und wartet, während der andere Thread regelmäßig prüft und ordnungsgemäß beendet wird. .NET 4 enthält eine eigens dafür entwickelte Struktur, die CancellationToken .

17
Adam Robinson

Sie sollten es nicht direkt versuchen. Gestalten Sie Ihre Aufgaben so, dass sie mit einem CancellationToken arbeiten, und stornieren Sie sie auf diese Weise.

Außerdem würde ich empfehlen, den Haupt-Thread zu ändern, damit er auch über ein CancellationToken funktioniert. Das Aufrufen von Thread.Abort() ist eine schlechte Idee - es kann zu verschiedenen Problemen führen, die sehr schwer zu diagnostizieren sind. Stattdessen kann dieser Thread dieselbe Annullierung wie Ihre Aufgaben verwenden - und dieselbe CancellationTokenSource kann verwendet werden, um den Abbruch von all Ihrer Aufgaben und Ihres Hauptthreads auszulösen.

Dies führt zu einem viel einfacheren und sichereren Design.

7
Reed Copsey

Um die Frage von Prerak K zur Verwendung von CancellationTokens zu beantworten, wenn keine anonyme Methode in Task.Factory.StartNew () 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, 

}
7
Jools

Ich verwende einen gemischten Ansatz, um eine Aufgabe abzubrechen. 

  • Erstens versuche ich es höflich abzubrechen, indem ich Cancellation verwende. 
  • Wenn es noch läuft (z. B. aufgrund eines Fehlers eines Entwicklers), benehmen Sie sich falsch und töten Sie es mit einer Old-School-Methode Abort .

Prüfen Sie 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();
        }
    }
}
5
Alex Klaus

Sie können eine CancellationToken verwenden, um zu steuern, ob die Aufgabe abgebrochen wird. Sprichst du darüber, dass es vor dem Start abgebrochen wird ("Ich habe es schon getan?") Oder es tatsächlich in der Mitte unterbrechen? Wenn erstere, kann die CancellationToken hilfreich sein; In letzterem Fall müssen Sie wahrscheinlich einen eigenen "Bail-Out" -Mechanismus implementieren und an geeigneten Stellen in der Taskausführung prüfen, ob Sie schnell versagen sollten (Sie können das CancellationToken trotzdem verwenden, um Ihnen zu helfen, aber es ist etwas mehr manuell).

MSDN hat einen Artikel zum Abbrechen von Aufgaben: http://msdn.Microsoft.com/en-us/library/dd997396.aspx

3
Hank

Task wird auf dem ThreadPool ausgeführt (zumindest wenn Sie die Standard-Factory verwenden), sodass das Abbrechen des Threads die Tasks nicht beeinflussen kann. Informationen zum Abbruch von Aufgaben finden Sie unter Aufgabe Stornieren auf Msdn.

2
Oliver Hanappi

Aufgaben haben erstklassige Unterstützung für die Stornierung über Stornierungstoken . Legen Sie Ihre Aufgaben mit Stornomarkern an und brechen Sie die Aufgaben über diese explizit ab.

2
Tim Lloyd

Ich habe CancellationTokenSource ausprobiert, kann das aber nicht. Und das habe ich auf meine eigene Art 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 Aufrufens 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();
        }
    }
}
0