2016-04-04 8 views
0

Ich habe einen Code, der Task.Run mit einem Cancel-Token verwendet.Verwenden eines CancellationToken für eine Task.Run

Hier ist mein Code:

public class TaskObject 
{ 
    CancellationTokenSource _source = new CancellationTokenSource(); 
    public async Task TaskAction() 
    { 
     var task = Task.Run(async delegate 
     { 
      await TaskRun(); 
     }, _source.Token); 

     //TaskCancel(); 

     try 
     { 
      task.Wait(); 
     } 
     catch (Exception ex) 
     { 

     } 
    } 
    public async Task TaskRun() 
    { 
     if (_source.IsCancellationRequested) 
     { 
      _source.Token.ThrowIfCancellationRequested(); 
     } 

     SpeechSynthesizer _speechSynthesizer = new SpeechSynthesizer(); 
     _speechSynthesizer.SpeakAsync("This is a test prompt"); 
    } 
    public void TaskCancel() 
    { 
     _source.Cancel(); 
    } 
} 

Wenn ich die TaskCancel() im TaskAction() nennen, die Aufgabe abgebrochen Ausnahme abgefangen wird.

Wenn ich die TaskCancel() von außerhalb des Objekts aufrufen, wird die abgebrochene Ausnahme nicht abgefangen.

Hier einige Code zu zeigen, wo die annullierte Ausnahme nicht abgefangen wird:

taskObject = new TaskObject(); 
await taskObject.TaskAction(); 
taskObject.TaskCancel(); 

Wie kann ich die TaskCancel() von außerhalb des Objekts aufrufen, so dass die annullierte Ausnahme abgefangen wird?

Antwort

4

Die Stornierung muss kooperativ sein. Task.Run hat keine Kenntnis davon, welche Nebenwirkungen der geplante Delegierte haben könnte, daher ist Task.RunTask.Run Ihr CancellationToken und wirft eine OCE (ohne Ihre Anwendung in einem potenziell inkonsistenten Zustand zu lassen) vor beginnt die asynchrone Operation - Nach diesem Punkt ist es der Benutzercode, um das Token zu überprüfen.

Problem ist, Ihr Benutzercode verbringt die meiste Zeit damit, SpeechSynthesizer.SpeakAsync zu erwarten, die keine Überladung hat, die eine CancellationToken akzeptiert.

SpeechSynthesizer hat jedoch eine SpeakAsyncCancelAll Methode, die Sie es in etwa so stecken könnte:

public async Task TaskRun(CancellationToken ct) 
{ 
    ct.ThrowIfCancellationRequested(); 

    SpeechSynthesizer _speechSynthesizer = new SpeechSynthesizer(); 

    using (ct.Register(() => _speechSynthesizer.SpeakAsyncCancelAll())) { 
     await _speechSynthesizer.SpeakAsync("This is a test prompt"); 
    } 
} 

kann ich Ihnen nicht sagen, ob dies eine Ausnahme erzeugen wird darauf hinweist, dass der Vorgang abgebrochen wurde - Sie Ich werde versuchen, es selbst zu sehen.

+0

Wenn ich den SpeechSynthesizer außer Acht lasse, kann ich sagen, dass es beim Start einer Task.Run keine Möglichkeit gibt, eine Abbruchanforderung zu erkennen, außer zu Beginn der Funktion? – user3736648

+0

@ user3736648, Sie scheinen die richtige Idee zu haben, aber die Formulierung braucht etwas Polnisch (siehe ersten Absatz). Einfach gesagt, 'Task.Run' überprüft das' CancellationToken' am Anfang, bevor Sie Ihren Delegaten im Thread-Pool ausführen. Sobald der Delegat-Aufruf ausgeführt wird, kann 'Task.Run' nichts mehr tun. –

+0

Danke. Anders als Task.Run gibt es eine korrekte Möglichkeit, einen Thread zu erstellen, der jederzeit abgebrochen werden kann? – user3736648

Verwandte Themen