perl 为什么“kill”不立即退出线程?

92vpleto  于 2022-11-15  发布在  Perl
关注(0)|答案(2)|浏览(158)

我正在尝试编写一个简单的脚本,该脚本会产生一个线程,该线程执行一个可能超时的任务。(为了编写一个简单的StackOverflow示例,我用sleep命令替换了实际的进程)。
这个程序生成一个线程,然后使用cond_timedwait来监视线程并检查它是否超时。如果发生超时,它将使用“STOP”信号调用线程上的kill方法,以通知线程它应该退出。

use strict;
use threads;
use threads::shared;
use warnings;

my $var :shared;

my $thread = threads->create(sub {

    # Tell the thread how to handle the STOP signal
    local $SIG{'STOP'} = sub {
        print "Stop signal received\n";
        threads->exit();
    };

    # Perform a process that takes some time
    sleep 10;

    # Signal that the thread is complete
    lock($var); cond_signal($var);
});

# Current time + 1 second
my $wait_time = time() + 1;
my $timeout;

{
    # Wait for the thread to complete or until a timeout has occurred
    lock($var); $timeout = !cond_timedwait($var, $wait_time);
}

# Check if a timeout occurred
if ($timeout) {
    print "A timeout has occurred\n";

    # Signal the thread to stop
    $thread->kill('STOP')->join();
}
else {
    $thread->join();
}

此代码成功运行并输出以下输出:
1秒过去......

A timeout has occurred

9秒过去......

Stop signal received

问题是,即使检测到超时并将“STOP”信号发送到线程,程序似乎仍在等待整整10秒,然后才打印“收到停止信号”并退出。
我试着改变它,使它在终止线程后调用detach而不是join,但“收到停止信号”消息从未被打印出来,这意味着程序在线程干净退出之前就退出了。我想确保线程确实被中断并退出,因为在真实的的程序中,我希望在发生超时后终止并重试进程,如果在分离的线程上已经运行了另一个示例,则该进程将无法工作。
如何使线程在收到“STOP”信号时立即打印消息并退出?

hs1ihplo

hs1ihplo1#

这些“信号”并不是实际的操作系统信号,有些操作不会被它们中断
注意:此模块提供的线程信号功能实际上并不通过操作系统发送信号。它在Perl级别模拟信号,以便在适当的线程中调用信号处理程序。例如,发送$thr->kill('STOP')实际上并不暂停线程(或整个进程),但会导致在该线程中调用$SIG{'STOP'}处理程序(如上所述)。
...
相应地,向线程发送信号不会中断线程当前正在执行的操作:信号将在当前操作完成后起作用。例如,如果线程在I/O调用上阻塞,向其发送信号不会导致I/O调用中断,因此信号将立即起作用。
没有说明“operation”的粒度,但sleep显然是不可中断的,因此信号处理程序仅在其完成后运行。

use warnings;
use strict;
use feature 'say';

use threads;

say "Start at ", scalar localtime, " (", time, ")";

my $thread = threads->create(sub {

    # Tell the thread how to handle the STOP signal
    $SIG{'STOP'} = sub {
        say "\tStop signal received. Exiting at ", time;
        threads->exit();
    };

    say "\tIn the thread ", threads->tid;

    # Perform a process that takes some time
    #sleep 10;
    do { sleep 1; say "\tnappin'... ($_ sec)" } for 1..10;
});

sleep 3;
$thread->kill('STOP')->join();  # works differently with detach()

say "Main thread done, exiting at ", time;

输出量

Start at Thu Jul  7 11:11:27 2022 (1657217487)
        In the thread 1
        nappin'... (1 sec)
        nappin'... (2 sec)
        Stop signal received. Exiting at 1657217490
Main thread done, exiting at 1657217490

使用detach而不是join时,它仍然会在正确的时间停止循环,但我看不到信号处理程序运行的迹象。(在我的测试中,我让信号处理程序也写一个文件,而使用detach时,它没有。)对于我来说,使用共享变量等,一切都是一样的,就像问题中一样。
当然,这个sleep并不重要--但它是一个警告,要仔细测试实际作业,信号的目的是停止。

nwlqm0z1

nwlqm0z12#

信号只能发送给进程。因此,$thread->kill('STOP')不可能发送一个实际的信号。因此,没有任何东西会中断sleep
在每个语句之间,Perl检查是否有一个“信号”进入。如果有,它就处理它。所以“信号”只在sleep完成后才被处理。
如果你有10个1秒的睡眠而不是1个10秒的睡眠,等待的时间最多是1秒。

相关问题