How can I stop async Process by CancellationToken?
Asked Answered
K

2

12

I found beneath code for execute some process without freezing UI. This code is executed when 'Start Work' button is pressed. And I think users would stop this work by 'Stop' button. So I found this article at MSDN.. https://msdn.microsoft.com/en-us/library/jj155759.aspx . But, It was hard that applying this CancellationToken at this code.. Anyone can help this problem?

I use public static async Task<int> RunProcessAsync(string fileName, string args) method only.

Code (From 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;
}

Usage :

var cancelToken = new CancellationTokenSource();
int returnCode = async RunProcessAsync("python.exe", "foo.py", cancelToken.Token);
if (cancelToken.IsCancellationRequested) { /* something */ }

When the start button clicked, it starts some python script. When script is running and user wants to stop it, user presses stop button. Then program executes below code.

cancelToken.Cancel();

Thank you very much for reading this question.

Kriegspiel answered 10/12, 2015 at 10:36 Comment(5)
Can I know why my question gave that -1 without any answer or comments?Kriegspiel
What do you want the CancellationToken to do? Kill the process?Jardiniere
@StephenCleary: Thank you for comment! Yes, What I want to do is Terminate program immediately.Kriegspiel
This question is not clear at all. There's not even a CancellationToken in the code you posted! Never mind do I see any sort of problem statement, other than maybe "it was hard". Please provide a good minimal reproducible example that reliably reproduces whatever problem you are having. Also provide a precise description of your problem: explain what the code does now and what you want it to do instead.Toomin
@PeterDuniho I added some short usage code. Thanks for advise!Kriegspiel
N
2

It's quite simple now:

process.WaitForExitAsync(token);
Netta answered 26/7, 2021 at 14:6 Comment(2)
await process.WaitForExitAsync(token); for completenessSusysuter
In the comments, the OP indicated that the cancellation should result in the external process being terminated, which constitutes the meat of @svick's answer. Cancelling the token passed to WaitForExitAsync() will not kill the external process, it just stops the currently running program from waiting for it. The CancellationTokenRegistration described above is still required.Conover
A
25

The simple answer is that you can just call process.Kill() when the token is canceled:

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

But there are two problems with this:

  1. If you attempt to kill a process that doesn't exist yet or that has already terminated, you get an InvalidOperationException.
  2. If you don't Dispose() the CancellationTokenRegistration returned from Register(), and the CancellationTokenSource is long-lived, you have a memory leak, since the registrations will stay in memory as long as the CancellationTokenSource.

Depending on your requirements, and your desire for clean code (even at the cost of complexity) it may be okay to ignore problem #2 and work around problem #1 by swallowing the exception in a catch.

Autodidact answered 11/12, 2015 at 14:39 Comment(2)
Should the call to the Register() method come before are after the call to process.Start()? How about in relation to the call to the Exited event? Can post as a separate question if recommended. My example is like your answer to #10789482Devy
you can also do the same this with less parenthesis: cancellationToken.Register(process.Kill);Tenpin
N
2

It's quite simple now:

process.WaitForExitAsync(token);
Netta answered 26/7, 2021 at 14:6 Comment(2)
await process.WaitForExitAsync(token); for completenessSusysuter
In the comments, the OP indicated that the cancellation should result in the external process being terminated, which constitutes the meat of @svick's answer. Cancelling the token passed to WaitForExitAsync() will not kill the external process, it just stops the currently running program from waiting for it. The CancellationTokenRegistration described above is still required.Conover

© 2022 - 2024 — McMap. All rights reserved.