C#并行运行多个非阻塞外部程序

时间:2011-04-17 11:19:03

标签: c# wpf multithreading process external

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

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

这是正确的做法吗?

另外,您是否建议使用良好的资源/文档来了解它的工作原理?我发现只有http://www.dotnetperls.com/threadpool

编辑:这些进程是基于网络的,即:运行时间可能会有很大差异,具体取决于链路延迟/带宽。

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

编辑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
}

这是正确的还是有更优雅的方式来实现这一目标? 我不希望在执行过程中阻止我的应用程序。 为此使用后台工作者会更好吗?

5 个答案:

答案 0 :(得分:4)

您应该使用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();

答案 1 :(得分:1)

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

正确的方法恕我直言,将计算在所有3个过程中完成的代理工作:

totalwork = time1 + time2 + time3

现在,如果你有多个处理器,它将需要更多像max(time1,time2,time3),但没关系。它是工作的代表。

为工作完成提供共享变量。每次进程执行更多工作时,通过计算work-done + = my-work-increment来更新进度条。进步只是工作完成/总工作。

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

答案 2 :(得分:0)

创建一个单独的线程。

在该线程中(伪代码):

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忙碌光标(永远旋转的圆圈)。

答案 3 :(得分:0)

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

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

答案 4 :(得分:0)

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

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

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

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

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

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