Как отменить асинхронную задачу, запускающую процесс на С#?

В настоящее время мой код выполняет проверку из svn и перенаправляет stdout и stderr в текстовое поле, используя две задачи, как показано ниже. Я хочу иметь возможность немедленно отменить задачу, когда пользователь нажимает кнопку StopButton, и иметь возможность отменить загрузку. Я понимаю, что если я изменил свою команду cmd.exe, чтобы она говорила что-то вроде «Пауза», которая продолжает работать до тех пор, пока пользователь что-то не щелкнет, я могу отменить эту команду с помощью StopButton. Я очень новичок в С# и пытаюсь понять, почему я могу отменить эту команду, а не загрузку svn.

Это способ, которым я должен реализовать свой код, или я должен попробовать другой метод?

Большое спасибо за помощь! Я действительно ценю это!

   public Form2()
    {
        InitializeComponent();

    }

    private TaskCompletionSource<bool> _cancelTask;

    private async Task RunProcess()
    {
        Process process = new Process
        {
            StartInfo = new ProcessStartInfo
            {
                FileName = "cmd.exe",
                Arguments = "svn download"
                UseShellExecute = false,
                RedirectStandardOutput = true,
                RedirectStandardError = true,
                CreateNoWindow = false,
            }
        };


        Console.Write(URL);
        process.Start();

        Task readerTasks = Task.WhenAll(
            ConsumeReader(process.StandardError),
            ConsumeReader(process.StandardOutput));

        Task completedTask = await Task.WhenAny(readerTasks, _cancelTask.Task);

        if(completedTask == _cancelTask.Task)
        {
            process.Kill();
            await readerTasks;
            throw new TaskCanceledException(_cancelTask.Task);
        }
    }

    private async Task ConsumeReader(TextReader reader)
    {
        char[] text = new char[512];
        int cch;

        while ((cch = await reader.ReadAsync(text, 0, text.Length)) > 0)
        {
            textBox2.AppendText(new string(text, 0, cch));
        }
    }

    private async void Submit_Click(object sender, EventArgs e)
    {

        Submit.Enabled = false;
        StopButton.Enabled = true;
        _cancelTask = new TaskCompletionSource<bool>();

        try
        {
            await RunProcess(cts.Token);
        }
        catch (OperationCanceledException)
        {
            MessageBox.Show("The operation was cancelled");
        }
        finally
        {
            _cancelTask = null;
            Submit.Enabled = true;
            StopButton.Enabled = false;
        }
    }

    private void StopButton_Click(object sender, EventArgs e)
    {
        _cancelTask.SetCanceled();
    }

person Ross Rhone    schedule 11.08.2016    source источник
comment
Вы отправляете сигнал уничтожения cmd, а не svn или любым другим дочерним процессам. Если вам нужен более структурированный подход к удалению деревьев внешних процессов, ознакомьтесь с Job API. Тем не менее, вам нужно будет сделать свой собственный p/Invokes, чтобы использовать его из .NET.   -  person Stephen Cleary    schedule 11.08.2016
comment
В дополнение к предыдущему комментарию обратите внимание, что если вы специально не хотите взаимодействовать с интерпретатором командной строки (cmd.exe), вы можете просто запустить svn напрямую, передав download в качестве аргумента этой задаче. Чтобы правильно ответить на приведенный выше вопрос, потребуется кто-то, кто на самом деле знает, что делает утилита svn с точки зрения процесса, и какие ее части все еще работают после того, как вы завершите процесс cmd.exe, который был запущен. начал это. Но я бы сначала попробовал просто запустить svn напрямую и посмотреть, достаточно ли этого.   -  person Peter Duniho    schedule 11.08.2016
comment
Стивен Клири и Питер Дунихо, вы совершенно правы. Если я изменю имя файла на svn.exe, эта задача завершится. Большое спасибо вам обоим за ваше время и усилия по этому вопросу!   -  person Ross Rhone    schedule 11.08.2016


Ответы (1)


Я нашел решение, не уверен, что это лучшее, но вы можете запустить другой процесс, чтобы запустить команду taskkill из командной строки, чтобы убить все запущенные .exe. Теперь я убиваю cmd.exe (родительский) и svn.exe (дочерний), которые были вызваны из cmd.exe!

Это полезно только в том случае, если вы знаете .exe, которые запускаются от родителя, если было создано много дочерних процессов, было бы лучше найти другое решение для рекурсивного перехода и поиска дочерних процессов.

    if(completedTask == _cancelTask.Task)
    {
        //process.Kill(); This would only kill the CMD.exe
        Process exit = new Process
            {
                StartInfo = new ProcessStartInfo
                {
                    FileName = "cmd.exe",
                    //Arguments = "/K Pause",
                    Arguments = "/C taskkill /f /im svn.exe & taskkill /f /im cmd.exe",
                    UseShellExecute = false,
                    RedirectStandardOutput = true,
                    RedirectStandardError = true,
                    CreateNoWindow = false,
                }
            };
            exit.Start();
        await readerTasks;
        throw new TaskCanceledException(_cancelTask.Task);
    }
person Ross Rhone    schedule 11.08.2016