如何通过 CancellationToken 停止异步进程?
How can I stop async Process by CancellationToken?
我在代码下方找到了执行某些进程而不冻结的代码 UI。按下 'Start Work' 按钮时执行此代码。我认为用户会通过 'Stop' 按钮停止这项工作。所以我在 MSDN.. https://msdn.microsoft.com/en-us/library/jj155759.aspx 找到了这篇文章。但是,很难在此代码中应用此 CancellationToken
..任何人都可以解决这个问题吗?
我只使用public static async Task<int> RunProcessAsync(string fileName, string args)
方法。
代码(来自 ):
public static async Task<int> RunProcessAsync(string fileName, string args)
{
using (var process = new Process
{
StartInfo =
{
FileName = fileName, Arguments = args,
UseShellExecute = false, CreateNoWindow = true,
RedirectStandardOutput = true, RedirectStandardError = true
},
EnableRaisingEvents = true
})
{
return await RunProcessAsync(process).ConfigureAwait(false);
}
}
// This method is used only for internal function call.
private static Task<int> RunProcessAsync(Process process)
{
var tcs = new TaskCompletionSource<int>();
process.Exited += (s, ea) => tcs.SetResult(process.ExitCode);
process.OutputDataReceived += (s, ea) => Console.WriteLine(ea.Data);
process.ErrorDataReceived += (s, ea) => Console.WriteLine("ERR: " + ea.Data);
bool started = process.Start();
if (!started)
{
//you may allow for the process to be re-used (started = false)
//but I'm not sure about the guarantees of the Exited event in such a case
throw new InvalidOperationException("Could not start process: " + process);
}
process.BeginOutputReadLine();
process.BeginErrorReadLine();
return tcs.Task;
}
用法:
var cancelToken = new CancellationTokenSource();
int returnCode = async RunProcessAsync("python.exe", "foo.py", cancelToken.Token);
if (cancelToken.IsCancellationRequested) { /* something */ }
单击开始按钮时,它会启动一些 python 脚本。
当脚本为 运行 并且用户想要停止它时,用户按下停止按钮。
然后程序执行下面的代码。
cancelToken.Cancel();
非常感谢您阅读这个问题。
简单的答案是当令牌被取消时你可以调用process.Kill()
:
cancellationToken.Register(() => process.Kill());
但这有两个问题:
- 如果您试图终止一个尚不存在或已经终止的进程,您会收到
InvalidOperationException
.
- 如果您不
Dispose()
CancellationTokenRegistration
从 Register()
返回,并且 CancellationTokenSource
是长期存在的,那么您有内存泄漏,因为注册只要 CancellationTokenSource
. 就会保留在内存中
根据您的要求,以及您对干净代码的渴望(即使以复杂性为代价),忽略问题 #2 并通过在 catch
中吞下异常来解决问题 #1 可能是可以的.
现在很简单了:
process.WaitForExitAsync(token);
我在代码下方找到了执行某些进程而不冻结的代码 UI。按下 'Start Work' 按钮时执行此代码。我认为用户会通过 'Stop' 按钮停止这项工作。所以我在 MSDN.. https://msdn.microsoft.com/en-us/library/jj155759.aspx 找到了这篇文章。但是,很难在此代码中应用此 CancellationToken
..任何人都可以解决这个问题吗?
我只使用public static async Task<int> RunProcessAsync(string fileName, string args)
方法。
代码(来自 ):
public static async Task<int> RunProcessAsync(string fileName, string args)
{
using (var process = new Process
{
StartInfo =
{
FileName = fileName, Arguments = args,
UseShellExecute = false, CreateNoWindow = true,
RedirectStandardOutput = true, RedirectStandardError = true
},
EnableRaisingEvents = true
})
{
return await RunProcessAsync(process).ConfigureAwait(false);
}
}
// This method is used only for internal function call.
private static Task<int> RunProcessAsync(Process process)
{
var tcs = new TaskCompletionSource<int>();
process.Exited += (s, ea) => tcs.SetResult(process.ExitCode);
process.OutputDataReceived += (s, ea) => Console.WriteLine(ea.Data);
process.ErrorDataReceived += (s, ea) => Console.WriteLine("ERR: " + ea.Data);
bool started = process.Start();
if (!started)
{
//you may allow for the process to be re-used (started = false)
//but I'm not sure about the guarantees of the Exited event in such a case
throw new InvalidOperationException("Could not start process: " + process);
}
process.BeginOutputReadLine();
process.BeginErrorReadLine();
return tcs.Task;
}
用法:
var cancelToken = new CancellationTokenSource();
int returnCode = async RunProcessAsync("python.exe", "foo.py", cancelToken.Token);
if (cancelToken.IsCancellationRequested) { /* something */ }
单击开始按钮时,它会启动一些 python 脚本。 当脚本为 运行 并且用户想要停止它时,用户按下停止按钮。 然后程序执行下面的代码。
cancelToken.Cancel();
非常感谢您阅读这个问题。
简单的答案是当令牌被取消时你可以调用process.Kill()
:
cancellationToken.Register(() => process.Kill());
但这有两个问题:
- 如果您试图终止一个尚不存在或已经终止的进程,您会收到
InvalidOperationException
. - 如果您不
Dispose()
CancellationTokenRegistration
从Register()
返回,并且CancellationTokenSource
是长期存在的,那么您有内存泄漏,因为注册只要CancellationTokenSource
. 就会保留在内存中
根据您的要求,以及您对干净代码的渴望(即使以复杂性为代价),忽略问题 #2 并通过在 catch
中吞下异常来解决问题 #1 可能是可以的.
现在很简单了:
process.WaitForExitAsync(token);