Perl:异步执行 10 个系统进程
Posted
技术标签:
【中文标题】Perl:异步执行 10 个系统进程【英文标题】:Perl: execute 10 system processes async 【发布时间】:2019-02-18 22:10:23 【问题描述】:这个问题是针对在 windows 2012 服务器上运行 perl 的。
所以我有一个名为 Commands_To_Run 的文件夹,下面有 100 个批处理文件,例如
Commands_To_Run
- run_1.bat
- run_2.bat
- run_3.bat
...
- run_100.bat
这些 run*.bat 文件中的每一个都需要大约 30 分钟才能完成。如果我使用 FOR 循环连续运行这些批处理文件,那么我需要 100 * 30 分钟才能运行。 (太长了!)
我想要的是编写一个 perl 脚本,它一次会执行 10 个批处理文件。一旦任何一个批处理文件完成,下一个批处理文件就会被执行。
例如我想通过run10.bat执行run1.bat。假设 run7.bat 完成然后我想运行下一个 run11.bat 等等。因此,在任何给定时间都有 10 个文件在运行。
我曾想过使用这个 perl 脚本来运行批处理文件,但这会同时运行所有 100 个文件,并且会杀死我的 windows CPU 和处理。
for ($x=0; $x < scalar(@files); $x++ )
$file=@files[$x];
chomp $file;
$cmd="start $file ";
print "Runnung Command is: $cmd\n";
system($cmd);
我查看了给出的建议,但没有关于如何使用 Forks::Super 的工作示例
【问题讨论】:
perl process queue的可能重复 你需要 GNU Parallel。 @pilcrow 我查看了给出的建议,但没有关于如何使用 Forks::Super 的工作示例。在我的情况下它将如何工作?没看懂 作为一个不相关的注释,您的 for 循环示例可以写得更糟糕:foreach my $file (@files) ...
(注意 foreach 和 for 实际上是同义词)
【参考方案1】:
在队列中并行运行进程的简单方法是使用Parallel::ForkManager
use warnings;
use strict;
use feature 'say';
use Parallel::ForkManager;
my $pm = Parallel::ForkManager->new(10);
# Prepare the list of your batch files (better get names from disk)
my @batch_files = map "Commands_To_Run/run_$_.bat" 1..100;
foreach my $batch_file (@batch_files)
$pm->start and next;
# Run batch job
say "Running: $batch_file";
#system($batch_file); # uncomment to actually run the jobs
$pm->finish;
$pm->wait_all_children;
这是一个最小但有效的脚本。例如,请参阅 this post 和 this post,了解有关作业如何进行的更多信息,尤其是有关如何从作业返回数据的信息。
注意:这不是核心模块,因此您可能需要安装它
【讨论】:
良好的工作脚本 - 为了完整性添加系统($batch_file);在运行行之后,它将实际执行我的 run_*.bat 文件 @SamB 感谢您的编辑,我已经根据您的意思对它们进行了一些调整。我像往常一样注释掉了实际运行工作的行;我喜欢让人们在启用实际批量运行之前检查事情!另一个流畅的部分是文件夹与脚本等相关的位置,但您可以调整它。 这里需要提到一件事,它等待所有孩子,即只有在前 10 个批次中的每个文件都处理完后,它才会处理下一个 10 个。抱歉,我太早接受了答案。 @SamB 嗯,不,它没有——wait_all_childern
只告诉它“收割”所有子进程完成后(因为所有 fork 的代码都必须; 在 Windows 上它可能会转到join
线程)。当它工作时,它会在一个退出时立即启动一个新进程;它始终保持在 10。(仅打印时看不到,因为它太快了!)
让我更彻底地测试一下。不过,感谢您的出色回答。【参考方案2】:
fmap_scalar function from Future::Utils 可以处理保持一定数量进程运行的所有逻辑,IO::Async::Process 可以异步运行和管理每个进程(鉴于它是 windows,我不确定所有这些是否会合理地工作):
use strict;
use warnings;
use IO::Async::Loop;
use Future::Utils 'fmap_scalar';
my @commands = ...;
my $loop = IO::Async::Loop->new;
my $f = fmap_scalar
my $cmd = shift;
my $f = $loop->new_future;
$loop->open_process(command => $cmd, on_finish => sub $f->done($_[1]) );
return $f;
foreach => \@commands, concurrent => 10;
my @exit_codes = $f->get; # starts the loop until all processes are done
【讨论】:
我安装了草莓,这就是我得到的——在@INC 中找不到 IO/Async/Loop.pm(您可能需要安装 IO::Async::Loop 模块) IO::Async 和 Future::Utils 都是非核心模块,需要安装。【参考方案3】:Parallel::ForkManager 依赖于fork
,这是 Unix 系统的一个特性,在 Windows 系统上由 Perl(使用线程)严重模拟。我建议直接使用线程。少这样会出错。
use threads;
use Thread::Queue 3.01 qw( );
sub worker
my ($command) = @_;
system($command);
my $q = Thread::Queue->new();
for (1..10)
async
while (my $job = $q->dequeue())
worker($job);
$q->enqueue($_) for @commands;
$q->end();
$_->join for threads->list;
【讨论】:
【参考方案4】:只是把它放在那里,但也可以使用批处理文件执行此操作,这应该循环遍历所有 .bat
文件,检查进程计数,如果进程不小于或等于则只启动新的9(如果等于 9,它仍然会踢一):
@echo off
setlocal enabledelayedexpansion
set cnt=1
for %%i in (*.bat) do (
set id=%%i
call :check
)
:check
for /f "tokens=1,*" %%a in ('tasklist /FI "WINDOWTITLE eq _process*" ^| find /I /C "cmd.exe"') do set procs=%%a
if !procs! leq 9 (
if not "!id!"=="%0" start "_process!cnt!" !id!
set /a cnt+=1
) else (
goto check
)
【讨论】:
以上是关于Perl:异步执行 10 个系统进程的主要内容,如果未能解决你的问题,请参考以下文章
linux 系统下oracle 10G perl进程cpu占用100% ,这个进程有啥用?能关掉吗?会不会有啥影响?
如何在 linux 中将 Perl 脚本作为系统守护进程运行?