2016-06-20 49 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

我一直在怀疑,并从审查代码,看起来像你是对的。我继承的这个框架看起来非常麻烦。将尝试修复并报告回来。 –