Есть ли асинхронный эквивалент процесса.Начать?


Как следует из названия, есть ли эквивалент Process.Start (позволяет запустить другое приложение или пакетный файл), что я мог ожидать?

Я играю с небольшим консольным приложением, и это казалось идеальным местом для использования async и await, но я не могу найти никакой документации для этого сценария.

то, что я думаю, что-то вроде этих строк:

void async RunCommand()
{
    var result = await Process.RunAsync("command to run");
}
3 100

3 ответа:

Process.Start() только запускает процесс, он не ждет, пока он закончится, так что это не имеет большого смысла, чтобы сделать это async. Если вы все еще хотите сделать это, вы можете сделать что-то вроде await Task.Run(() => Process.Start(fileName)).

но, если вы хотите асинхронно ждать завершения процесса, вы можете использовать the Exited событие вместе с TaskCompletionSource:

static Task<int> RunProcessAsync(string fileName)
{
    var tcs = new TaskCompletionSource<int>();

    var process = new Process
    {
        StartInfo = { FileName = fileName },
        EnableRaisingEvents = true
    };

    process.Exited += (sender, args) =>
    {
        tcs.SetResult(process.ExitCode);
        process.Dispose();
    };

    process.Start();

    return tcs.Task;
}

вот мое мнение, основанное на svick по. Он добавляет перенаправление вывода, сохранение кода выхода и немного лучшую обработку ошибок (удаление Process объект, даже если он не может быть запущен):

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

вот еще один подход. Аналогичная концепция svick и Охад это ответы, но с помощью метода расширения на Process тип.

метод расширения:

public static Task RunAsync(this Process process)
{
    var tcs = new TaskCompletionSource<object>();
    process.EnableRaisingEvents = true;
    process.Exited += (s, e) => tcs.TrySetResult(null);
    // not sure on best way to handle false being returned
    if (!process.Start()) tcs.SetException(new Exception("Failed to start process."));
    return tcs.Task;
}

пример использования в методе, содержащем:

public async Task ExecuteAsync(string executablePath)
{
    using (var process = new Process())
    {
        // configure process
        process.StartInfo.FileName = executablePath;
        process.StartInfo.UseShellExecute = false;
        process.StartInfo.CreateNoWindow = true;
        // run process asynchronously
        await process.RunAsync();
        // do stuff with results
        Console.WriteLine($"Process finished running at {process.ExitTime} with exit code {process.ExitCode}");
    };// dispose process
}