【问题标题】:PERL - Multithreading - Children + grandchildren - limit of threads runningPERL - 多线程 - 子 + 孙 - 运行线程的限制
【发布时间】:2014-10-04 06:41:29
【问题描述】:

我制作了一个 Perl 脚本,在其中创建线程(同时运行的线程受到限制),每个线程创建自己的子节点,这些子节点的数量也应该受到限制。

在我托管我的脚本的地方,我不能同时为每个 Perl 脚本启动超过 X 个线程。在下面的示例中,我同时最多有 X = 3 x 7 = 21 个线程。

  • 第一份工作 3 ($nb_process_first)
  • 第二份工作 7 ($nb_process_second)

问题:

  1. 是否有更好的方法来管理线程及其子项? (例如队列 - 请给我一些代码示例,因为我尝试过但没有成功)
  2. 尽管我在所有正在运行的线程上使用循环来加入它们(参见脚本末尾),但我当前的脚本并未终止并加入所有线程。

#!/usr/bin/perl -s

use threads;

my @threads;
my $nb_process_first = 3;
my @running          = ();

print "START" . "\n";

$current = 1;
while ( $current <= 10 ) {
    @running = threads->list(threads::running);
    if ( scalar @running < $nb_process_first ) {
        print "Launch firstJob=" . scalar @running . "\n";
        my $thread = threads->create( \&firstJob );
        push( @threads, $thread );
    } else {
        redo;
    }
    $current++;
}

my @joinable = threads->list(threads::joinable);
while ( scalar @joinable != 0 ) {
    foreach my $thr ( threads->list() ) {
        $thr->join();
    }
    @joinable = threads->list(threads::joinable);
}

print "END" . "\n";

sub secondJob {
    for ( $i = 0; $i <= 15; $i++ ) {
        print "secondJob=" . $i . "\n";
        sleep 1;
    }
    threads->exit();
}

sub firstJob {
    my $nb_process_second = 7;
    my @running           = ();
    $current = 1;
    while ( $current <= 10 ) {
        @running = threads->list(threads::running);
        if ( scalar @running < $nb_process_second ) {
            print "firstJob/Launch secondJob=" . scalar @running . "-" . $current . "\n";
            my $secondthread = threads->create( \&secondJob );
            push( @threads, $secondthread );
            sleep 2;
        }
        $current++;
    }
    threads->exit();
}

【问题讨论】:

    标签: multithreading perl


    【解决方案1】:

    Thread::Queue 是线程代码的基本“工作线程”模型的便捷模型。

    有点像这样:

    #!/usr/bin/perl
    use strict;
    use warnings;
    use threads;
    use Thread::Queue;
    
    my $firstworkitem_q = Thread::Queue -> new();
    my $secondworkitem_q = Thread::Queue -> new();
    my $nthreads = 10; 
    
    sub first_worker {
       while ( my $item = $firstworkitem_q -> dequeue() ) {
           print "First worker picked up $item, and queues it to second worker\n";
           $secondworkitem_q -> enqueue ( $item ); 
       }
    }
    
    sub second_worker {
       while ( my $item = $secondworkitem_q -> dequeue() ) {
           print "Second worker got $item"; 
       }
    }
    
    my @first_workers;
    for ( 1..$nthreads ) {
        my $thr = threads -> create ( \&first_worker );
        push ( @first_workers, $thr );
    }
    
    for ( 1..$nthreads ) {
        my $thr = threads -> create ( \&second_worker );
    }
    
    $firstworkitem_q -> enqueue ( @things_to_processs );
    $firstworkitem_q -> end;
    
    foreach my $firstworker ( @first_workers ) {
       $firstworker -> join();
    }
    
    #here all the first workers have finished, so we know nothing will be queued to second work queue. 
    $secondworkitem_q -> end();
    
    foreach my $thr ( threads -> list() ) {
        $thr -> join();
    }
    

    你把东西塞进队列,然后迭代处理。当您 end 队列时,while 循环将获得 undef 并因此终止 - 使您的线程可连接。

    您不需要像您那样跟踪@running,因为threads -&gt; list() 会这样做。更重要的是 - 你需要使 @running 成为一个共享变量并锁定它,否则你在每个线程中都有它的不同副本。

    拥有firstJob spawn secondJob 我会避开,因为它会产生各种水果虫。我建议产生两类工作线程。使用$queue -&gt; end() 触发第一组工人关闭。

    【讨论】:

    • 非常感谢 Sobrique 的意见。我对这个代码示例有几个问题,以确保我已经很好地理解了它。 1)我不明白第一个工人的队列中放了什么:things_to_processs 列表应该是这个工人 first_workers 的列表吗? 2)我不明白这一行 my $thr = threads -> create ( \&worker );为什么我们不创建线程-> 创建(\&first_worker); ? 3)我不明白第二个工作线程是在哪里创建的?我希望你不介意,英语不是我的第一语言,我希望我的问题不要粗鲁。
    • 即使有一些“可能的修复”,我在之前的评论中谈到,脚本运行不正常。如果你能帮我找一个合适的。谢谢。例如:无法通过包“Thread::Queue”定位对象方法“end”
    • 创建 - 错字。应该是 first_worker。已编辑。如果未找到“结束”,请更新 Thread::Queue。 perl -MCPAN -e shellinstall Thread::Queue.
    • 列表@things_to_process 是您在线程中“执行”的任何内容。如果您要从数据库中获取用户 ID 列表,那将不会很好地并行化,但如果您有多个数据库 - 您可以将位置输入队列,然后将返回的用户 ID 传递到第二个队列。
    • 你说得对——我没有启动第二个工作线程。代码已编辑。 (如果需要,您可以更改“for”循环的大小)。
    【解决方案2】:

    至于你的第二个问题,线程只有在它们完成运行后才能加入(参见this answer)。因为在第二个while 循环运行时某些线程没有完成,所以它在没有加入它们的情况下完成。

    您的循环应该根据活动线程的数量而不是可连接线程的数量来等待。像这样的:

    while (threads->list() > 0)
    {
       foreach my $joinable (threads->list(threads::joinable))
       {
           $joinable->join();
       }
    }
    

    至于第一个问题,当然还有其他管理线程的方法。但是,在不了解您的任务的情况下,不可能说出您应该做什么。

    【讨论】:

    • 第一个:我从我的数据库中获得一组用户,对于每个用户,我启动一堆线程来获取个人信息,对于每个个人信息,我启动第二组线程来获取其他用户信息取决于首先收到的这些个人信息...
    • @Seb,从这个描述中,不清楚你是否应该使用线程。 “获取个人信息”任务涉及什么?
    • * 第一个线程负责在我的数据库中获取与他们负责的当前用户相关联的 id 列表。我使用 DBI 连接来获取这些 ID。 * 由第一个线程启动的第二个线程用于请求一个内部网站,其中我们有每个 id 的个人信息(my $request = HTTP::Request->new(GET => $url); 其中 $url 是Intranet url 的参数集是从我的第一个线程收到的 id)。
    • 示意图:从 DB 获取用户 --> 获取每个用户的 id 列表 --> 通过请求 Intranet 站点获取每个 id 的个人信息列表。其中,每个箭头都是一堆线程,我可以同时启动多少个线程。
    • 有点迂腐,但在 while 循环中你不需要 '>0' - 如果你在这样的标量上下文中测试一个列表,如果列表中有元素,它会返回 'true',并且'false' 如果没有。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-12-17
    • 1970-01-01
    • 1970-01-01
    • 2012-02-17
    • 2014-06-21
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多