如何在Perl中运行并发线程?

9
以下内容似乎不会像我预期的那样在并发线程中运行,而是每个进程都会阻塞,直到它完成为止。
my @arr = (1,2,3,4);
foreach (@arr) {
   threads->new(\&doSomething, $_)->join;
}

sub doSomething {
    my $thread = shift;
    print "thread $thread\n";
    sleep(5);
}

换句话说,它似乎执行的方式与非线程版本相同:
my @arr = (1,2,3,4);
foreach (@arr) {
   doSomething($_);
}

我正在运行ActivePerl v5.10.1 mswin32-x86-multi-thread。

如何在perl中运行并发线程?

6个回答

13

你必须在创建它们之后进行连接,而不是在创建它们时进行连接:

my @arr = (1,2,3,4);
my @threads;
foreach (@arr) {
   push @threads, threads->new(\&doSomething, $_);
}
foreach (@threads) {
   $_->join();
}

13
请参见 perldoc threads
问题在于调用 join() 函数将等待线程完成。您需要生成线程,然后在此之后进行加入操作,而不是一次性执行生成和加入。
进一步查看 perldoc threads 中的说明:
  • threads->list()
  • threads->list(threads::all)
  • threads->list(threads::running)
  • threads->list(threads::joinable)
当没有参数(或使用 threads::all)并处于列表上下文中时,返回所有未加入、未分离的线程对象列表。在标量上下文中,返回相同的计数。
如果使用真实的参数(使用 threads::running),则返回所有未加入、未分离的仍在运行的线程对象列表。
如果使用假值参数(使用 threads::joinable),则返回所有未加入、未分离的已完成运行的线程对象列表(即不会阻塞 ->join() 操作的线程)。
您可以使用此功能循环和列出线程,在可能的情况下进行加入,直到所有线程都完成(您可能需要设置等待时间的上限,在此之后将它们终止并中止)。

我尝试过这种方式——一次性生成所有线程,然后通过迭代和加入来进行控制,但是一旦第一个 join 被调用,它就会阻塞。好像我需要等待每个线程完成后再执行 join,但我不太确定如何实现。 - user210757
4
没错,join 是一个阻塞操作。但是已经被创建的线程在主进程等待 join 返回时仍将继续运行。在这种情况下,如果你先生成所有线程再进行 join,你会期望第一个 join 调用大约需要 5 秒钟时间,而其他所有的 join 调用则非常短。 - mob
list()与is_joinable的组合正是我所需要的 - 谢谢 - user210757

3
如果您不关心线程生成的输出,可以在线程上使用 ->detach()。
我的 @arr = (1,2,3,4); 我的 @threads; foreach (@arr) { 我的 $t = threads->new(\&doSomething, $_); $t->detach(); }
当一个线程完成后,它会被回收,您不需要调用 join()。
这种模式适用于创建工作线程,您不需要向其报告。

3

join(不仅在Perl中如此)会导致调用线程等待另一个线程完成。因此,您的示例生成线程,等待其完成,然后生成另一个线程,因此您会得到没有生成任何线程的印象。


那我该如何实现呢? - user210757

1

在启动所有线程之后,您必须调用join

perl -mthreads -le'$_->join for map threads->new(sub{print"Thread $_";sleep(2)}),1..4'

0
my @arr = (1,2,3,4);
my @threads;
foreach (@arr) {
   push @threads, threads->new(\&doSomething, $_);
}
foreach (@threads) {
   $_->join();
}

上述代码在小规模下运行良好。但如果您有数十个或更多的线程,您的操作系统开始停顿。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接