c# - 多任务环境中的流程执行

标签 c# process task

我正在尝试在我的程序中执行一些命令,因为我正在使用 System.Diagnostics.Process。我设法让它工作,当我 1 到 1 执行命令时,返回是正确的。然后我尝试通过为每个流程执行创建任务来加快流程,这就是我遇到问题的地方。

这是执行命令的类:

class ProcessExec
{
    public string Start(string command)
    {
        string res = "";

        Process process = new Process();
        process.EnableRaisingEvents = true;
        process.StartInfo.FileName = "powershell.exe";
        process.StartInfo.Arguments = command;
        process.StartInfo.UseShellExecute = false;
        process.StartInfo.RedirectStandardOutput = true;
        process.StartInfo.CreateNoWindow = true;

        process.OutputDataReceived += (object sender, DataReceivedEventArgs e) =>
        {
            res = res + e.Data;
        };

        process.Start();
        process.BeginOutputReadLine();
        process.WaitForExit(10000);

        return res;
    }
}

这是我的主要内容:

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("Start");

        List<Task> tasks = new List<Task>();

        ProcessExec exec = new ProcessExec();

        Stopwatch sw = new Stopwatch();
        sw.Start();

        string res1 = "";
        tasks.Add(Task.Run(() => { res1 = exec.Start("date"); }));
        string res2 = "";
        tasks.Add(Task.Run(() => { res2 = exec.Start("hostname"); }));
        string res3 = "";
        tasks.Add(Task.Run(() => { res3 = exec.Start("date"); }));
        string res4 = "";
        tasks.Add(Task.Run(() => { res4 = exec.Start("date"); }));
        string res5 = "";
        tasks.Add(Task.Run(() => { res5 = exec.Start("date"); }));
        string res6 = "";
        tasks.Add(Task.Run(() => { res6 = exec.Start("ipconfig"); }));
        string res7 = "";
        tasks.Add(Task.Run(() => { res7 = exec.Start("date"); }));
        string res8 = "";
        tasks.Add(Task.Run(() => { res8 = exec.Start("date"); }));

        Task.WaitAll(tasks.ToArray());

        sw.Stop();

        Console.WriteLine(sw.Elapsed.TotalSeconds);

        Console.WriteLine("1 - " + res1);
        Console.WriteLine("2 - " + res2);
        Console.WriteLine("3 - " + res3);
        Console.WriteLine("4 - " + res4);
        Console.WriteLine("5 - " + res5);
        Console.WriteLine("6 - " + res6);
        Console.WriteLine("7 - " + res7);
        Console.WriteLine("8 - " + res8);

        Console.WriteLine("End");
        Console.ReadKey();
    }
}

这是我的输出:

Start
7,4867498
1 - 22 de julho de 2017 10:25:46    
2 -    
3 - 22 de julho de 2017 10:25:48
4 - 22 de julho de 2017 10:25:48    
5 -    
6 -    
7 - 22 de julho de 2017 10:25:48
8 - 22 de julho de 2017 10:25:48
End

现在,我认为我的问题与不同线程中的 OutputDataReceived 事件有关,但我不完全确定。任何人都知道问题是什么以及我该如何解决?

最佳答案

如果您不仅能解释得到的输出,还能解释期望的输出,那就更好了。就是说,您要问的似乎确实是为什么您的某些(但不是全部)命令输出为空。是的,那是因为这些命令显然比您分配的 10 秒要长,所以您的方法在读取输出之前返回。

事实是,如果您希望您的方法在进程完成之前不返回,则根本没有理由使用 WaitForExit()。我知道这听起来有悖常理,但您必须在此处使用 WaitForExit() 的原因是您已选择异步使用流程输出。但没有理由这样做,因为您希望进程的处理是同步的。

所以,就这样做吧。您可以调用 Process.StandardOutput.ReadToEnd(),它将阻塞直到进程退出,然后返回所有输出。此外,您的类似乎没有任何理由是非静态的,因为除了方法中的本地状态之外,它没有任何状态。

所以像这样的东西会更好:

static class ProcessExec
{
    public static string Start(string command)
    {
        Process process = new Process();
        process.StartInfo.FileName = "powershell.exe";
        process.StartInfo.Arguments = command;
        process.StartInfo.UseShellExecute = false;
        process.StartInfo.RedirectStandardOutput = true;
        process.StartInfo.CreateNoWindow = true;

        process.Start();

        return process.StandardOutput.ReadToEnd();
    }
}

关于您的实现,我要更改的另一件事是局部变量的使用。如果您使用 Task,最好在 Task 对象本身中表示命令的结果,而不是为您执行的每个命令捕获局部变量。

例如:

static void Main(string[] args)
{
    Console.WriteLine("Start");

    List<Task<string>> tasks = new List<Task<string>>();

    Stopwatch sw = new Stopwatch();
    sw.Start();

    tasks.Add(Task.Run(() => ProcessExec.Start("date")));
    tasks.Add(Task.Run(() => ProcessExec.Start("hostname")));
    tasks.Add(Task.Run(() => ProcessExec.Start("date")));
    tasks.Add(Task.Run(() => ProcessExec.Start("date")));
    tasks.Add(Task.Run(() => ProcessExec.Start("date")));
    tasks.Add(Task.Run(() => ProcessExec.Start("ipconfig")));
    tasks.Add(Task.Run(() => ProcessExec.Start("date")));
    tasks.Add(Task.Run(() => ProcessExec.Start("date")));

    Task.WaitAll(tasks);

    sw.Stop();

    Console.WriteLine(sw.Elapsed.TotalSeconds);

    Console.WriteLine(string.Join(Environment.NewLine,
        tasks.Select((t, i) => $"{i + 1} - {t.Result}")));

    Console.WriteLine("End");
    Console.ReadKey();
}

那么您就不需要这些变量,并且可以使用循环变量轻松检索命令结果,而不必单独命名每个变量。

关于c# - 多任务环境中的流程执行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45252920/

相关文章:

使用 CSharpCodeProvider 进行 C# 运行时编译

javascript - 使用 Gulp 复制文件目录和文件

c# - 使用 BinaryReader 在 .NET 中重定向标准输出 - 检测 EOS/强制超时

Java Cron4J 持久任务

dependencies - 编译后执行的任务

c# - 同一服务器上多个 WebJob 的 Azure WebJob 应用程序洞察

c# - 在屏幕上绘制字符串 C#

c# - 如何使用udp从C#客户端与C服务器通信?

子进程写入文件

javascript - 使用 javascript 在 2 个 google chrome 进程(选项卡)之间进行通信