2016-06-20 95 views
0

我試圖啓用Kill選項作爲Process與調用線程異步運行的情況。我的例子看起來像@ svick的回答here。我正試圖在此post中實施@svick的建議。但是當我在UI中點擊Kill時,它看起來什麼都不做(即,過程簡單地像往常一樣完成)。使用CancellationToken停止進程

根據@ TimCopenhaver的迴應,這是預期的。但是如果我說出來了,它仍然沒有做任何事情,這次是因爲CancellationTokenSource對象cts爲空,這是意外的,因爲我在嘗試殺死它之前在​​類的Dispatch方法中實例化它。這裏是我的代碼片段:

UI類:

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); 
    } 
} 

​​類:

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; 
    } 

​​類:

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類(這裏是我調用CancellationTokenRegister方法,但不知道究竟在何處放置它,假設它事項):

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; 
} 

感謝您的關注和欣賞的任何想法。

+0

'ctoken.Register'返回應該在'Exited'事件處理程序中處理的對象。 –

+2

這應該基本上工作(除了一些競爭條件可能不會導致你的直接問題)。在cts.Cancel和process.Kill上放置斷點,看看它出錯的地方。平分。 – usr

+0

您能否爲CaseDispatcher顯示您的IsRunning屬性的代碼?這些設置在哪裏? –

回答

1

我認爲IsRunning屬性是問題所在。由於TaskCompletionSource並不真正知道您啓動了外部過程,因此它處於WaitingForActivation狀態。這裏有一個簡單的例子來說明:

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); 
} 

通知將一直說WaitingForActivation,直到它切換到RanToCompletion。有關詳情,請參見this answer。總之,如果任務是由TaskCompletionSource創建的,它將永遠不會進入運行狀態。你將不得不自己管理IsRunning屬性。

+0

從我迄今爲止學到的,對於使用Process的批處理流程,我們必須使用TaskCompletionSource異步運行從調用線程,正確?這意味着如果我們想要殺死這樣的進程,這個問題就沒有辦法解決了。另外,當我調試這個時,我注意到cts在Kill方法中也是空的,所以再次,它不進入,即使cts被實例化,這對我來說是意外的。有關於此的任何想法? –

+1

您仍然可以取消,但沒有解決方法無法檢查正在運行的任務的狀態。對於cts爲空的第二個問題,我懷疑GetCaseDispatcher中存在一個錯誤。如果CaseDispatcher實例沒有被正確緩存,你可能會得到一個沒有設置屬性的新實例。 –

+0

我一直在懷疑,並從審查代碼,看起來像你是對的。我繼承的這個框架看起來非常麻煩。將嘗試修復並報告回來。 –