如何通过 CancellationToken 停止异步进程?

问题描述 投票:0回答:3

我发现下面的代码可以在不冻结 UI 的情况下执行某些进程。当按下“开始工作”按钮时执行此代码。我认为用户会通过“停止”按钮来停止这项工作。所以我在MSDN上找到了这篇文章..https://msdn.microsoft.com/en-us/library/jj155759.aspx。但是,在这段代码中应用这个

CancellationToken
很难。任何人都可以解决这个问题吗?

我只使用

public static async Task<int> RunProcessAsync(string fileName, string args)
方法。

代码(来自https://stackoverflow.com/a/31492250):

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

非常感谢您阅读这个问题。

c# async-await cancellation
3个回答
24
投票

简单的答案是,当令牌被取消时,您只需调用

process.Kill()
即可:

cancellationToken.Register(() => process.Kill());

但是这有两个问题:

  1. 如果您尝试终止尚不存在或已终止的进程,您会得到
    InvalidOperationException
  2. 如果您不
    Dispose()
    CancellationTokenRegistration
    返回
    Register()
    ,并且
    CancellationTokenSource
    是长期存在的,那么就会出现内存泄漏,因为只要
    CancellationTokenSource
    ,注册就会保留在内存中。

根据您的要求以及您对干净代码的渴望(即使以复杂性为代价),可以忽略问题 #2 并通过将异常吞入

catch
来解决问题 #1。


2
投票

现在很简单:

process.WaitForExitAsync(token);

0
投票

问题是即使使用

await process.WaitForExitAsync(token);

通过调用

cts.Cancel()
,将会抛出异常,并且
CancellationTokenRegistration
将根本不起作用。 我也不明白这是如何运作的。

© www.soinside.com 2019 - 2024. All rights reserved.