Perl线程中的垃圾收集

pco*_*ley 7 perl multithreading garbage-collection

这个问题是好奇心,因为下面的两个项目之一有效.

我正在使用Image :: Magick调整大量照片的大小.为了节省一些时间,我在自己的线程中处理每张照片,并使用信号量来限制同时工作的线程数.最初我允许每个线程一次运行,但脚本会快速为所有照片分配3.5 GB(我只有2GB可用),并且由于所有交换到磁盘,脚本运行速度比正常慢5倍.

工作,信号量版本代码看起来像这样:

use threads;
use Thread::Semaphore;
use Image::Magick;

my $s = Thread::Semaphore->new(4);
foreach ( @photos ) {
    threads->create( \&launch_thread, $s );
}
foreach my $thr ( reverse threads->list() ) {
    $thr->join();
}

sub launch_thread {
    my $s = shift;
    $s->down();
    my $image = Image::Magick->new();

    # do memory-heavy work here

    $s->up();
}
Run Code Online (Sandbox Code Playgroud)

这样可以快速分配500MB,运行得非常好而且不需要更多.(线程以相反的顺序连接以构成一个点.)

我想知道是否可能会同时启动80个线程并阻止大部分线程的开销,所以我改变了我的脚本以阻止主线程:

my $s = Thread::Semaphore->new(4);
foreach ( @photos ) {
    $s->down();
    threads->create( \&launch_thread, $s );
}
foreach my $thr ( threads->list() ) {
    $thr->join();
}

sub launch_thread {
    my $s = shift;
    my $image = Image::Magick->new();

    # do memory-heavy work here

    $s->up();
}
Run Code Online (Sandbox Code Playgroud)

此版本开始正常,但逐渐累积原始版本使用的3.5GB空间.它比一次运行所有线程更快,但仍然比阻塞线程慢一点.

我的第一个猜测是,在调用join()之前,线程使用的内存不会被释放,并且因为它是阻塞的主线程,所以在它们全部被分配之前不会释放任何线程.但是,在第一个工作版本中,线程以或多或少的随机顺序传递保护,但以相反的顺序连接.如果我的猜测是正确的,那么,除了四个正在运行的线程之外,还应该等待任何时候加入(),并且此版本也应该更慢.

那么为什么这两个版本如此不同呢?

ike*_*ami 4

您不需要创建超过 4 个线程。一个主要好处是这意味着 Perl 解释器的副本数量减少了 76 个。此外,它使得收割顺序变得毫无意义,因为所有线程或多或少同时完成。

use threads;
use Thread::Queue qw( );
use Image::Magick qw( );

use constant NUM_WORKERS => 4;

sub process {
   my ($photo) = @_;
   ...
}

{
   my $request_q = Thread::Queue->new();

   my @threads;
   for (1..NUM_WORKERS) {
       push @threads, async {
          while (my $photo = $request_q->dequeue()) {
             process($photo);
          }
       };
   }

   $request_q->enqueue($_) for @photos;
   $request_q->enqueue(undef) for 1..NUM_THREADS;
   $_->join() for @threads;
}
Run Code Online (Sandbox Code Playgroud)