2016-06-20 16 views
0

Ich versuche, eine Kill Option für einen Fall zu aktivieren, der asynchron vom aufrufenden Thread als Process ausgeführt wird. Mein Beispiel sieht wie @ svicks Antwort here aus. Ich versuche die Empfehlung von @svick in diesem post zu implementieren. Aber wenn ich in der Benutzeroberfläche auf Kill klicke, scheint es nichts zu tun (d. H. Der Prozess läuft einfach wie gewöhnlich zum Abschluss).Einen Prozess mit CancellationToken stoppen

Gemäß @ TimCopenhavers Antwort wird dies erwartet. Aber wenn ich das kommentierte, tut es immer noch nichts, dieses Mal, weil das CancellationTokenSource Objekt cts null ist, was unerwartet ist, da ich es in der Dispatch Methode der Klasse instanziiere, bevor ich versuche, es zu töten. Hier ist mein Code-Schnipsel:

UI Klasse:

private void OnKillCase(object sender, EventArgs args) 
{ 
    foreach (var case in Cases) 
    { 
     Args caseArgs = CaseAPI.GetCaseArguments(case); 
     CaseDispatcher dispatcher = CaseAPI.GetCaseDispatcher(case); 
     dispatcher.Kill(); 
     CaseAPI.Dispose(caseArgs); 
    } 
} 

CaseDispatcher Klasse:

private Task<bool> task; 
    private CancellationTokenSource cts; 
    public override bool IsRunning 
    { 
     get { return task != null && task.Status == TaskStatus.Running; } 
    } 
    public override void Kill() 
    { 
     //if (!IsRunning) 
     //{ 
     // return; 
     //} 
     if (cts != null) 
     { 
      cts.Cancel(); 
     } 
    } 
    public override async Task<bool> Dispatch() 
    { 
     cts = new CancellationTokenSource(); 
     task = CaseAPI.Dispatch(Arguments, cts.Token); 
     return await task; 
    } 

CaseAPI Klasse:

public static async Task<bool> Dispatch(CaseArgs args, CancellationToken ctoken) 
{ 
    bool ok = true; 
    BatchEngine engine = new BatchEngine() 
     { 
      Spec = somespec, 
      CaseName = args.CaseName, 
      CaseDirectory = args.CaseDirectory 
     }; 
    ok &= await engine.ExecuteAsync(ctoken); 
    return ok; 
} 

BatchEngine Klasse (hier ist, wo ich die CancellationToken aufrufen Register Methode, aber nicht sicher, wo genau zu positionieren, vorausgesetzt, es zählt):

public virtual Task<bool> ExecuteAsync(CancellationToken ctoken) 
{ 
    var tcs = new TaskCompletionSource<bool>(); 
    string exe = Spec.GetExecutablePath(); 
    string args = string.Format("--input={0} {1}", Input, ConfigFile); 

    try 
    { 
     var process = new Process 
     { 
      EnableRaisingEvents = true, 
      StartInfo = 
      { 
       UseShellExecute = false, 
       FileName = exe, 
       Arguments = args, 
       CreateNoWindow = true, 
       RedirectStandardOutput = true, 
       RedirectStandardError = true, 
       WorkingDirectory = CaseDirectory 
      } 
     }; 
     ctoken.Register(() => 
      { 
       process.Kill(); 
       process.Dispose(); 
       tcs.SetResult(false); 
      }); 
     process.Exited += (sender, arguments) => 
     { 
      if (process.ExitCode != 0) 
      { 
       string errorMessage = process.StandardError.ReadToEnd(); 
       tcs.SetResult(false); 
       tcs.SetException(new InvalidOperationException("The batch process did not exit correctly. Error message: " + errorMessage)); 
      } 
      else 
      { 
       File.WriteAllText(LogFile, process.StandardOutput.ReadToEnd()); 
       tcs.SetResult(true); 
      } 
      process.Dispose(); 
     }; 
     process.Start(); 
    } 
    catch (Exception e) 
    { 
     Logger.InfoOutputWindow(e.Message); 
     tcs.SetResult(false); 
     return tcs.Task; 
    } 
    return tcs.Task; 
} 

Danke für Ihr Interesse und schätzt irgendwelche Ideen zu diesem Thema.

+0

'ctoken.Register' gibt ein Objekt zurück, das im Ereignishandler' Exited' entsorgt werden sollte. –

+2

Dies sollte grundsätzlich funktionieren (abgesehen von einigen Race-Bedingungen, die wahrscheinlich nicht Ihre unmittelbare Problem verursachen). Setzen Sie Haltepunkte auf cts.Cancel und process.Kill, um zu sehen, wo es schief geht. Halbieren. – usr

+0

Können Sie den Code Ihrer IsRunning-Eigenschaft für CaseDispatcher anzeigen? Wo wird das eingestellt? –

Antwort

1

Ich glaube, die IsRunning-Eigenschaft ist das Problem. Da die TaskCompletionSource nicht wirklich weiß, dass Sie den externen Prozess gestartet haben, bleibt er im Status "WaitingForActivation" hängen. Hier ist ein vereinfachtes Beispiel zu demonstrieren:

var tsc = new TaskCompletionSource<int>(); 

Task.Factory.StartNew(() => 
{ 
    Thread.Sleep(10000); 
    tsc.SetResult(10); 
}); 

var tmp = tsc.Task; 

TaskStatus status = tmp.Status; 
while (status != TaskStatus.RanToCompletion) 
{ 
    status = tmp.Status; 
    Thread.Sleep(1000); 
    Console.WriteLine(status); 
} 

Hinweis es halten wird sagen WaitingForActivation, bis es zu RanToCompletion schaltet. Weitere Informationen hierzu finden Sie unter this answer. Kurz gesagt, wenn die Task von einer TaskCompletionSource erstellt wird, wird sie niemals in den aktiven Zustand versetzt. Sie müssen die IsRunning-Eigenschaft selbst verwalten.

+0

hingewiesen Von was ich bisher gelernt, für Batch-Prozesse, die den Prozess verwenden, müssen TaskCompletionSource asynchron ausgeführt werden vom aufrufenden Thread, richtig? Dies bedeutet, dass es kein Problem gibt, wenn wir solche Prozesse beenden wollen. Auch wenn ich dies debugge, merke ich, dass die cts auch Null in der Kill-Methode ist, und so wieder, es tritt nicht ein, obwohl CTS instanziiert ist, war dies für mich unerwartet. Irgendwelche Ideen dazu? –

+1

Sie können den Vorgang noch abbrechen, es gibt jedoch keine Problemumgehung, da der Status der ausgeführten Aufgabe nicht überprüft werden kann.Für das zweite Problem, bei dem cts null ist, vermute ich einen Fehler in GetCaseDispatcher. Wenn die CaseDispatcher-Instanz nicht ordnungsgemäß zwischengespeichert wird, erhalten Sie möglicherweise eine neue Instanz zurück, für die die Eigenschaften nicht festgelegt sind. –

+0

Ich vermutete das gleiche und von der Überprüfung des Codes sieht aus, als ob du wieder Recht hast. Dieser Rahmen, den ich geerbt habe, scheint sehr fehlerhaft zu sein. Wird versuchen, das Problem zu beheben und zu melden. –