这个问题很有趣,因为以下两个程序之一有效。
我正在使用 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() 之前不会被释放,并且由于它是阻塞的主线程,因此在所有线程都被分配之前不会释放线程。然而,在第一个工作版本中,线程以或多或少的随机顺序通过保护,但以相反的顺序加入。如果我的猜测是正确的,那么,在任何时候都应该有不止四个正在运行的线程在等待加入(),而且这个版本也应该更慢。
那么为什么这两个版本如此不同呢?