Perl:异步执行10个系统进程

时间:2019-02-18 22:10:23

标签: perl batch-file

此问题用于在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的有效示例

4 个答案:

答案 0 :(得分:3)

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

答案 1 :(得分:3)

使用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 postthis post,详细了解作业的进行方式,尤其是如何从作业中返回数据。

注意:这不是核心模块,因此您可能需要安装

答案 2 :(得分:1)

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;
}

答案 3 :(得分:0)

只需将其放到那里,但是也可以使用批处理文件来执行此操作,这应该遍历所有.bat文件,检查进程计数,并且如果进程不少于或仅检查新文件,等于9(如果等于9,则仍会踢出1):

@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
 )