c# - 处置已注册的 CancellationTokenRegistration 以终止以异步方法启动的进程
问题描述
我正在使用一种async
方法来启动Process
(调用raspistill
)。该代码非常标准,但就像这个问题中的开发人员一样:如何通过 CancellationToken 停止异步进程?,我希望能够使用CancellationToken
. 例如,我的 ASP.NET 核心 API 端点之一启动了一个需要关闭的 MJPEG 流。
我已经接受了上述问题的答案,并编写了以下 ProcessRunner 类:
using System;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using NLog;
namespace RabbieCam.Utils
{
/// <summary>
/// Run process as asynchronous task.
/// </summary>
public static class ProcessRunner
{
private static Logger logger = LogManager.GetCurrentClassLogger();
/// <summary>
/// Run process as asynchronous task.
/// </summary>
public static async Task<int> RunProcessAsync(string fileName,
string args,
CancellationToken token)
{
CancellationTokenRegistration registration;
using (var process = new Process
{
StartInfo =
{
FileName = fileName,
Arguments = args,
UseShellExecute = false,
CreateNoWindow = true,
RedirectStandardOutput = true,
RedirectStandardError = true
},
EnableRaisingEvents = true
})
{
var exitCode = await RunProcessAsync(
process,
token,
out registration
).ConfigureAwait(false);
logger.Debug($"Process {fileName} ended");
registration.Dispose();
logger.Debug("Registration disposed");
return exitCode;
}
}
/// <summary>
/// Run process asynchronously.
/// </summary>
private static Task<int> RunProcessAsync(
Process process,
CancellationToken token,
out CancellationTokenRegistration registration
)
{
var tcs = new TaskCompletionSource<int>();
process.Exited += (s, ea) => tcs.SetResult(process.ExitCode);
// process.OutputDataReceived += (s, ea) => // TODO if needed
// process.ErrorDataReceived += (s, ea) => // TODO if needed
registration = token.Register(() =>
{
try
{
process.Kill();
}
catch (InvalidOperationException e)
{
logger.Error($"Failed to kill process: {e.Message}");
}
}
);
bool started = process.Start();
if (!started)
{
registration.Dispose();
throw new InvalidOperationException(
"Could not start process: " + process
);
}
process.BeginOutputReadLine();
process.BeginErrorReadLine();
return tcs.Task;
}
}
}
我的问题是registration
,一旦流程结束或被取消,这是否会根据需要进行处理?
还有没有更好的解决问题的方法?
我想 CancellationTokenSource 也是在 ASP.NET API 请求完成后处理的吗?如果是这种情况,则可能不需要清理注册。
解决方案
我写了一个单元(ish)测试,上面发布的代码似乎按预期工作。当我取消进程并处理注册时,我会看到调试语句。
public class TestProcessRunner
{
[Fact]
public void TestRegistrationDisposedOnCancel()
{
var cts = new CancellationTokenSource();
var token = cts.Token;
string fileName = "ping";
string args = "google.com";
var task = ProcessRunner.RunProcessAsync(fileName, args, token);
cts.Cancel();
task.Wait();
}
}
推荐阅读
- jms - 无法使用连接模式“客户端”和主机名连接到队列管理器“DevQueue01”
- gtk3 - 构建一个 GTK Gridview,就像一个不同高度的画廊
- office-js - Office js EmailAddressDetails undefined recipientType for Mac
- c# - 尝试对 ASN.1 模式对象执行“__set__”操作
- c++ - 我的代码在构建时没有出现任何错误,但无法正常工作
- google-workspace - 如何使用 PHP 脚本在 gsuite 上为教育创建自定义域电子邮件地址
- c++ - C++:`enable_if` 来限制支持特定算术运算的类型
- python - 使用烧瓶的聊天室
- json - JSON序列化是确定性的吗?
- r - 如何将两列连接到 R 中的一列向量/列表中?