Multithreading 如何在Perl中实现信号量线程通信?

Multithreading 如何在Perl中实现信号量线程通信?,multithreading,perl,process,multiprocessing,semaphore,Multithreading,Perl,Process,Multiprocessing,Semaphore,我的Perl脚本需要同时运行多个线程 …这样的线程需要从web获取一些信息,所以使用了HTTP::Async 但有些线程只有在其他线程这样说时才需要访问web 可能有一个或多个线程等待“go”信号,并且可能有一个或多个这样的“go”信号可以发送的线程。开始时,信号量的状态为“等待”,一旦变为“开始”,它将保持这种状态 最后,应用程序检查最大运行时间。如果线程运行时间过长,将发送自终止信号 现在开门见山。我的问题是: 如何在脚本中最有效地编写等待“信号量”的代码(请参见上面脚本中的注释)。我是否

我的Perl脚本需要同时运行多个线程

…这样的线程需要从web获取一些信息,所以使用了
HTTP::Async

但有些线程只有在其他线程这样说时才需要访问web

可能有一个或多个线程等待“go”信号,并且可能有一个或多个这样的“go”信号可以发送的线程。开始时,信号量的状态为“等待”,一旦变为“开始”,它将保持这种状态

最后,应用程序检查最大运行时间。如果线程运行时间过长,将发送自终止信号


现在开门见山。我的问题是:

  • 如何在脚本中最有效地编写等待“信号量”的代码(请参见上面脚本中的注释)。我是否应该简单地将just shared变量与一些伪
    sleep
    循环一起使用

  • 我是否需要在应用程序末尾添加一些
    sleep
    循环,以便让线程有时间进行自我销毁

  • 您可以查看以执行此工作。您可以设置一个队列来处理等待“go”信号的线程和发送“go”信号的线程之间的信令。这是一个我还没有测试过的快速模型:

    ...
    use Thread::Queue;
    ...
    # In main body
    my $q = Thread::Queue->new();
    ...
    $thread = threads->create(
        sub {
               local $SIG{KILL} = sub { threads->exit };
               my $url = shift;
               if ($url ... ) {
                 # wait for "go" signal from other threads
                 my $mesg = $q->dequeue();
                 # you could put in some termination code if the $mesg isn't 'go'
                 if ($mesg ne 'go') { ... }
               }
               ...
               if ($data ... ) {
                 # send "go" signal to waiting threads
                 $q->enqueue('go');
               }
             }
           }, $_);
    ...
    
    需要等待“go”信号的线程将在dequeue方法上等待,直到有东西进入队列。消息进入队列后,一个线程(只有一个线程)将获取消息并对其进行处理

    如果希望停止线程,使其无法运行,可以向队列的头部插入停止消息

    $q->insert(0, 'stop') foreach (@threads);
    
    Thread::Queue和CPAN发行版中的一些示例更详细地说明了这一点

    对于你的第二个问题,答案是,不幸的是,这要视情况而定。当您继续终止线程时,需要什么样的清理才能实现干净的关闭?如果地毯被从线下拉出,最坏的情况是什么?您可能希望在任何时候计划进行清理。您可以做的另一个选择是等待每个线程实际完成

    我的评论询问您是否可以删除
    detach
    调用的原因是,此方法允许主线程退出,而不关心任何子线程发生了什么。相反,如果您删除此呼叫并添加:

    $_->join() foreach threads->list();
    
    在主块的末尾,这将要求主应用程序等待每个线程实际完成


    如果将
    detach
    方法留在原位,那么如果需要线程执行任何类型的清理,则需要在代码结束时休眠。在线程上调用
    detach
    时,您告诉Perl的是,当主线程退出时,您不关心线程在做什么。如果主线程退出,并且仍在运行的线程已分离,则程序将在没有警告的情况下完成。但是,如果您不需要任何清理,并且仍然调用
    detach
    ,请随时退出。

    试试这样的方法

    #!/usr/bin/perl
    
    use threads;
    use threads::shared;
    
    $|=1;
    
    my ($global):shared;
    my (@threads);
    
    push(@threads, threads->new(\&mySub,1));
    push(@threads, threads->new(\&mySub,2));
    push(@threads, threads->new(\&mySub,3));
    
    $i = 0;
    
    foreach my $myThread(@threads)
    
    {
        my @ReturnData = $myTread->join ;
        print "Thread $i returned: @ReturnData\n";
        $i++;
    }
    
    sub mySub
    {
        my ($threadID) = @_;
    
        for(0..1000)
        {
            $global++;
            print "Thread ID: $threadID >> $_ >> GLB: $global\n";
            sleep(1);
        }   
        return( $id );
    }
    

    这个问题有一个公开的赏金价值+50声誉。请改进你的答案。我发现你的帖子很有趣,但是你没有回答我帖子中的第二个子问题(如果/如何等待线程自毁)@user1215106我注意到你的代码中有一个
    $thread->detach。通常,您使用它来忽略线程,而不关心它是否完成。你有理由把它放在那里吗?或者它可以被移除吗?我相信它可以被移除。我等不及了!无限的为了让线程完成,我必须为其他应用程序释放资源。如果没有按时完成,线程会在
    $response=$async->wait_for_next_response
    行等待。@user1215106查看最后一段,让我知道这是否为第二个问题提供了更好的答案。我是否正确理解您使用的是单独的HTTP::async对象(由新线程复制,而不是共享)每个线程一次最多获取一个URL?@pilcrow-是的,看起来像。这是在浪费资源吗?它可能会或不会减少内存或时间效率,但真正的消耗是在程序员的周期上。:)设计很难理解,因此可能需要安全地更改/扩展,因为组件似乎不太正确。@pilcrow-所以建议在线程内部分配HTTP::Async?我相信没有理由分享这个目标。除非用这种方法会更优化。我不知道怎么评论。您向我们展示了您的实现的一个摘录,您是如何尝试做一些事情的,而不是您尝试做什么。我只能观察到HTTP::Async是一种用于并行单线程web请求的工具,这似乎与我们看到的设计部分不一致。
    ...
    use Thread::Queue;
    ...
    # In main body
    my $q = Thread::Queue->new();
    ...
    $thread = threads->create(
        sub {
               local $SIG{KILL} = sub { threads->exit };
               my $url = shift;
               if ($url ... ) {
                 # wait for "go" signal from other threads
                 my $mesg = $q->dequeue();
                 # you could put in some termination code if the $mesg isn't 'go'
                 if ($mesg ne 'go') { ... }
               }
               ...
               if ($data ... ) {
                 # send "go" signal to waiting threads
                 $q->enqueue('go');
               }
             }
           }, $_);
    ...
    
    $q->insert(0, 'stop') foreach (@threads);
    
    $_->join() foreach threads->list();
    
    #!/usr/bin/perl
    
    use threads;
    use threads::shared;
    
    $|=1;
    
    my ($global):shared;
    my (@threads);
    
    push(@threads, threads->new(\&mySub,1));
    push(@threads, threads->new(\&mySub,2));
    push(@threads, threads->new(\&mySub,3));
    
    $i = 0;
    
    foreach my $myThread(@threads)
    
    {
        my @ReturnData = $myTread->join ;
        print "Thread $i returned: @ReturnData\n";
        $i++;
    }
    
    sub mySub
    {
        my ($threadID) = @_;
    
        for(0..1000)
        {
            $global++;
            print "Thread ID: $threadID >> $_ >> GLB: $global\n";
            sleep(1);
        }   
        return( $id );
    }