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

发布于 2024-11-02 01:47:22 字数 920 浏览 1 评论 0原文

我需要从我的应用程序运行外部可执行文件的多个实例。该可执行文件的平均运行时间约为 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
}

它是正确的还是有更优雅的方法来实现这一点? 当然,我不希望我的应用程序在执行过程中被阻止。 为此使用后台工作者是否更好?

I need to run several instances of an external executable from my app. The average run time for this executable is about 3 minutes.
I want to redirect output from these processes, and update a progress bar in my GUI.
Of course I don't want to wait for them to return before I can continue using my app.

I think I should create a thread for every instance, and update my progress bar when a thread finishes.

Is this the right approach ?

Also, do you recommend a good resource / documentation to understand how it works ? I've found http://www.dotnetperls.com/threadpool only.

edit : these processes are network-based, ie: the run time may vary a lot depending on the link latency/bandwidth.

Concerning the progress bar, I would like to update it every time a process finishes. Is there a handler for that ? Later i will add more detailed update, based on the processes output to increase the progress done at each execution step.

edit 2 :

Thanks for your inputs. As I may have to run a lot of process (up to 20), and I don't want to saturate bandwidth, i'll run 5 in parallel max. Every time a process finishes, I increment the progress counter (for my progress bar) and I run another one until they're all completed, using :

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
}

Is it correct or is there a more elegant way to achieve this ?
I don't want my app to be blocked during execution of course.
Is it better to use background workers for this ?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

ゞ花落谁相伴 2024-11-09 01:47:22

您应该使用 ProcessProcessStartInfo
您需要将 ProcessStartInfo.UseShellExecute 设置为 false,将 ErrorDialog 设置为 false,将 RedirectStandardOutput 设置为 false code> 到 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();

You should be using Process and ProcessStartInfo.
You'll need to set ProcessStartInfo.UseShellExecute to false, ErrorDialog to false, RedirectStandardOutput to true (and possibly RedirectStandardError too).

You'll also need to provide a delegate to the Process object to handle to output generated by the external process through OutputDataReceived (and possibly ErrorDataReceived as well).

There's also an Exited delegate you can set that will be called whenever the process exits.

Example:

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();
望笑 2024-11-09 01:47:22

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

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

totalwork = time1 + time2 + time3

现在,如果您有多个处理器,则需要更像 max(time1, time2, time3) 但那就是好的。这是工作的代表。

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

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

Just waiting for each thread to end before updating the progress bar results in nothing happening ... then a quick jump .. 3 times. You may as well skip the progress bar.

The correct way to do it IMHO would be to calculate the toal work done across all 3 process:

totalwork = time1 + time2 + time3

Now, if you have multiple processors, it will take more like max(time1, time2, time3) but thats ok. It's a representation of work.

Have a shared variable for work-done. Each time a process does some more work, update the progress bar by calculating work-done += my-work-increment. The progress is just work-done/totalwork.

This will give good results regardless of whether the threads run sequentially or in parallel. Since you don't know how things are going to run (you might have a single processor cpu) this is the best approach.

海螺姑娘 2024-11-09 01:47:22

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

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

Just create several instances of the Process class by calling the constructor, set the properties to redirect output stream, and then start them.

Your program won't wait for the called process to exit, as long as you don't call the WaitForExit method. No multi-threading is required.

榕城若虚 2024-11-09 01:47:22

创建单个线程。

在该线程(伪代码)中:

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

Create a single thread.

In that thread (pseudocode):

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

See http://msdn.microsoft.com/en-us/library/ty0d8k56.aspx for wait for exit

Or... do you want to run the external apps in parallel?

Edit: based on the latest updates in the orginal post:

If you don't know the actual progress then don't use a regular progress bar. How about an infinite progress bar or a "working" icon?

An infinite progress bar could be a progress bar that fills up and the starts from beginning until everything is done. A working icon is like the Windows busy cursor (the ever spinning circle).

说好的呢 2024-11-09 01:47:22

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

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

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

完成后,每个后台工作程序应从 ObservableCollection 中删除其 TaskProgressInfo,从而从 UI 中删除进度条。

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

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

How about creating an ObservableCollection of TaskProgressInfo,
where TaskProgressInfo is a custom class, to which you write your progress.

Bind a WPF listview to that collection, using a datatemplate (with target type = TaskProgressInfo) to show a progressbar for each item (task).

Create an array of BackgroundWorkers that launch the external app and monitor it.
Each background worker should update its TaskProgressInfo, thus updating the datasource of a progressbar.

Upon completion each BackgroundWorker should remove its TaskProgressInfo from the ObservableCollection, thus removing a progress bar from the UI.

Since BackgroundWorker uses the background (UI) thread for report progress and completion,
the changes to the ObservableCollection will be done by its creating thread (thread safe).

Behind the scenes, .NET will use a ThreadPool - some backgroundworkers will share threads.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文