multithreading - Perl 线程中的垃圾收集

标签 multithreading perl 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();
}

这很快分配了 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();
}

这个版本开始很好,但逐渐积累了原始版本使用的3.5GB空间。它比一次运行所有线程快,但仍然比阻塞线程慢很多。

我的第一个猜测是线程使用的内存在调用 join() 之前不会被释放,并且由于它是阻塞的主线程,因此在所有线程都被分配之前不会释放线程。然而,在第一个工作版本中,线程以或多或少的随机顺序通过守卫,但以相反的顺序加入。如果我的猜测是正确的,那么在任何时候等待 join() 的线程应该不止四个,而且这个版本也应该更慢。

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

最佳答案

您不需要创建超过 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;
}

关于multithreading - Perl 线程中的垃圾收集,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12753208/

相关文章:

c# - volatile 与 volatile 读/写?

java - thread local的用途和需求是什么

php - mysql 或 perl 函数匹配 Asterisk 拨号方案模式

perl - 使用 Config::General 解析错误文件时捕获问题

c# - 如何检测并发 GC 是否在 .Net 中运行

java - 通过引用对象改进 GC

java - `jcmd PID GC.class_histogram`在收集数据之前会调用完整GC吗?

ruby 线程#allocate TypeError

java - 共享资源先到先得政策

android - 自动创建插入语句以填充预创建数据库中的联结表