You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Perl多线程中Alarm::Concurrent的setalarm未触发处理函数问题

为什么Alarm::Concurrent的setalarm没有触发超时逻辑?

这个问题我之前排查过类似的,核心原因是你对Alarm::Concurrent的回调执行环境理解有误:这个模块的回调函数是在它自己创建的独立闹钟线程里运行的,不是在你调用setalarm的工作线程中

你在回调里写的die "Timedout\n"只会终止那个后台的闹钟线程,完全不会干扰到你的工作线程——工作线程里的sleep会老老实实跑完指定的5秒,然后执行clearalarm,自然走正常退出的分支,这就是为什么所有线程都输出了EXITED

怎么验证这个?

你可以给回调加一行打印线程ID的代码,一眼就能看明白:

setalarm 3, sub { 
    print "Alarm thread TID: " . threads->tid() . "\n";
    die "Timedout\n";
};

运行后会发现,输出的闹钟线程ID和你的工作线程ID完全不一样,说明这俩根本不是同一个线程,工作线程的eval当然捕获不到另一个线程里抛出的异常。

修复方案:让闹钟线程给工作线程发信号

要让超时逻辑真正作用于工作线程,我们需要让闹钟线程向工作线程发送信号,触发工作线程内部的异常。修改后的代码如下:

use threads ('yield', 'stack_size' => 64*4096, 'exit' => 'threads_only', 'stringify', 'signal');
use Alarm::Concurrent qw(setalarm clearalarm);

threads->create(\&MyThreadFunc,1);
threads->create(\&MyThreadFunc,1);
threads->create(\&MyThreadFunc,5);
threads->create(\&MyThreadFunc,5);

sub MyThreadFunc {
    my $result = "Never Ran";
    my $timeToSleep = $_[0];
    my $tid = threads->tid();
    my $self_thread = threads->self();

    # 给当前工作线程注册SIGALRM信号处理:收到信号就抛超时异常
    $SIG{ALRM} = sub { die "Timedout\n" };

    eval {
        # 闹钟触发时,向当前工作线程发SIGALRM信号
        setalarm 3, sub { $self_thread->signal('ALRM') };
        sleep ($timeToSleep);
        clearalarm; # 正常执行完就取消闹钟
        $result = "TID $tid EXITED\n";
    };

    # 恢复默认的信号处理,避免影响其他逻辑
    $SIG{ALRM} = 'DEFAULT';

    if ($@ eq "Timedout\n") {
        $result = "TID $tid TIMED OUT\n";
    } elsif ($@) {
        $result = "$@";
    }
    print "Thread returning $result";
    return $result;
}

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

修复逻辑拆解

  1. 先在工作线程里注册SIGALRM信号处理函数,收到信号就抛出Timedout异常,让eval能捕获到。
  2. setalarm的回调改成向当前工作线程发送SIGALRM信号,而不是直接die——这样信号会投递到目标工作线程。
  3. Perl的sleep函数会被信号打断,收到SIGALRM后立刻退出睡眠,触发信号处理函数抛出异常,被eval捕获后进入超时分支。
  4. 最后恢复默认的信号处理,避免对后续代码造成意外影响。

这样修改后,就能实现你预期的效果:TID1、2正常退出,TID3、4触发超时。

内容的提问来源于stack exchange,提问作者BarkerChippy

火山引擎 最新活动