【问题标题】:C# Run multiple non-blocking external programs in parallelC# 并行运行多个非阻塞外部程序
【发布时间】:2011-04-17 11:19:03
【问题描述】:

我需要从我的应用程序中运行多个外部可执行文件实例。此可执行文件的平均运行时间约为 3 分钟。 我想重定向这些进程的输出,并更新我的 GUI 中的进度条。 当然,我不想等他们回来再继续使用我的应用程序。

我认为我应该为每个实例创建一个线程,并在线程完成时更新我的​​进度条。

这是正确的方法吗?

另外,您是否推荐一个好的资源/文档来了解它的工作原理?我只找到了http://www.dotnetperls.com/threadpool

edit :这些进程是基于网络的,即:运行时间可能会因链路延迟/带宽而有很大差异。

关于进度条,我想在每次进程完成时更新它。有处理程序吗?稍后我将根据流程输出添加更详细的更新,以增加每个执行步骤的进度。

编辑 2:

感谢您的意见。因为我可能需要运行很多进程(最多 20 个),并且我不想使带宽饱和,所以我最多并行运行 5 个。每次进程完成时,我都会增加进度计数器(对于我的进度条)并运行另一个计数器,直到它们全部完成,使用:

Process p = new Process();
p.StartInfo.FileName = pathToApp;
p.EnableRaisingEvents = true;
p.Exited += OnCalibrationProcessExited;
p.Start();

private void OnCalibrationProcessExited(object sender, EventArgs e)
{
  runAnotherOne function
}

这是正确的还是有更优雅的方法来实现这一点? 我当然不希望我的应用程序在执行期间被阻止。 为此使用后台工作人员会更好吗?

标签: c# wpf multithreading process external


【解决方案1】:

您应该使用ProcessProcessStartInfo。 您需要将ProcessStartInfo.UseShellExecute 设置为false,将ErrorDialog 设置为false,将RedirectStandardOutput 设置为true(也可能设置RedirectStandardError)。

您还需要为 Process 对象提供一个委托,以处理外部进程通过OutputDataReceived(也可能是ErrorDataReceived)生成的输出。

您还可以设置一个Exited 委托,该委托将在进程退出时被调用。

例子:

ProcessStartInfo processInfo = new ProcessStartInfo("Write500Lines.exe");
processInfo.ErrorDialog = false;
processInfo.UseShellExecute = false;
processInfo.RedirectStandardOutput = true;
processInfo.RedirectStandardError = true;

Process proc = Process.Start(processInfo);
proc.ErrorDataReceived += (sender, errorLine) => { if (errorLine.Data != null) Trace.WriteLine(errorLine.Data); };
proc.OutputDataReceived += (sender, outputLine) => { if (outputLine.Data != null) Trace.WriteLine(outputLine.Data); };
proc.BeginErrorReadLine();
proc.BeginOutputReadLine();

proc.WaitForExit();

【讨论】:

  • 你让我免于浪费大量时间。
【解决方案2】:

在更新进度条之前等待每个线程结束不会导致任何事情发生......然后快速跳转.. 3 次。你也可以跳过进度条。

恕我直言,正确的做法是计算所有 3 个过程完成的总工作量:

总工作量 = time1 + time2 + time3

现在,如果您有多个处理器,则需要更多类似 max(time1, time2, time3) 但没关系。这是一种工作的表现。

有一个用于完成工作的共享变量。每次进程完成更多工作时,通过计算 work-done += my-work-increment 来更新进度条。进度只是工作完成/全部工作。

无论线程是顺序运行还是并行运行,这都会产生良好的结果。由于您不知道事情将如何运行(您可能有一个单处理器 cpu),这是最好的方法。

【讨论】:

    【解决方案3】:

    只需调用构造函数创建Process类的几个实例,设置属性重定向输出流,然后启动它们。

    只要您不调用 WaitForExit 方法,您的程序就不会等待被调用的进程退出。不需要多线程。

    【讨论】:

      【解决方案4】:

      创建一个线程。

      在那个线程中(伪代码):

      Thread begins here
      for each externalApp
         Run the application with redirect output 
         Wait for exit
         Update progress bar
      end for
      Thread ends here
      

      查看http://msdn.microsoft.com/en-us/library/ty0d8k56.aspx等待退出

      或者...您想并行运行外部应用程序吗?

      编辑:基于原始帖子中的最新更新:

      如果您不知道实际进度,请不要使用常规进度条。无限进度条或“工作”图标怎么样?

      一个无限的进度条可以是一个填满的进度条,从头开始直到一切完成。工作图标就像 Windows 忙碌光标(不断旋转的圆圈)。

      【讨论】:

      • 嗨维克多,确实这似乎是一个更好的方法。我编辑了我的信息,因为它不清楚。但是在你的伪代码表示中,如果我等待退出,进程将不会并行运行,所以我不会像@Shyc2001 建议的那样使用 WaitForExit。
      【解决方案5】:

      如何创建一个 TaskProgressInfo 的 ObservableCollection,
      其中 TaskProgressInfo 是一个自定义类,您可以在其中写入进度。

      使用数据模板(目标类型 = TaskProgressInfo)将 WPF 列表视图绑定到该集合,以显示每个项目(任务)的进度条。

      创建一个启动外部应用程序并对其进行监控的 BackgroundWorkers 数组。
      每个后台工作人员都应更新其 TaskProgressInfo,从而更新进度条的数据源。

      完成后,每个 BackgroundWorker 应从 ObservableCollection 中删除其 TaskProgressInfo,从而从 UI 中删除进度条。

      由于 BackgroundWorker 使用后台 (UI) 线程来报告进度和完成, 对 ObservableCollection 的更改将由其创建线程完成(线程安全)。

      在幕后,.NET 将使用 ThreadPool - 一些后台工作人员将共享线程。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-06-23
        • 1970-01-01
        • 2015-10-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多