为什么“杀人”不是立即退出线程吗?

发布于 2025-02-13 18:49:07 字数 1480 浏览 0 评论 0原文

我正在尝试编写一个简单的脚本,该脚本催生了执行可能超时任务的线程。 (为了编写一个简单的stackoverflow示例,我用sleep命令代替了实际过程)。

该程序产生线程,然后使用cond_timedwait监视线程并检查是否已计时。如果发生超时,它将用“停止”信号在线程上调用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

问题是,即使检测到超时并将“停止”信号发送到线程,该程序似乎仍然是等待整个10秒钟,然后打印“收到的停止信号”并退出。

我尝试更改它,以便调用分离,而不是join杀死线程后,但是从未打印过“停止信号”消息,这意味着该程序正在退出线程干净退出。我想确保该线程实际上被中断并退出,因为在实际程序中,我将要在超时发生后杀死和重试该过程,并且如果已经在独立线程上运行另一个实例,则该过程将无法工作。

我该如何使线程立即打印消息并在接收“停止”信号时退出?

I am trying to write a simple script that spawns a thread that performs a task that may timeout. (For the sake of writing a simple example for StackOverflow I replaced the actual process with a sleep command).

This program spawns a thread and then uses a cond_timedwait to monitor the thread and check if it has timed out. If a timeout occurs it calls the kill method on the thread with a "STOP" signal to notify the thread that it should exit.

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();
}

This code runs successfully and prints the following output:

1 second passes...

A timeout has occurred

9 seconds pass...

Stop signal received

The problem is, even though a timeout is detected and the "STOP" signal is sent to the thread, the program still seems to be waiting the full 10 seconds before printing "Stop signal received" and exiting.

I tried changing it so it calls detach instead of join after killing the thread, but then the "Stop signal received" message is never printed which means the program is exiting before the thread cleanly exits. I want to make sure the thread is actually interrupted and exits, because in the real program I will want to kill and retry the process after the timeout has occurred and the process won't work if there is another instance already running on a detached thread.

How can I make it so the thread instantly prints the message and exits when it receives the "STOP" signal?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

冧九 2025-02-20 18:49:07

这些“信号” 不是实际的操作信号,他们赢得了操作'打断

警告:该模块提供的线程信号能力实际上并未通过OS发送信号。它在Perl级别上模拟信号,以便在适当的线程中调用信号处理程序。例如,发送$ thr-&gt; kill('stop')实际上不会暂停线程(或整个过程),但确实会导致$ sig {'stop'} < /code>在该线程中调用的处理程序(如上图所示)。
...
相应地,将信号发送到线程不会破坏该线程当前正在使用的操作:当当前操作完成后,信号将采用。例如,如果该线程卡在I/O调用上,则发送信号不会导致I/O呼叫中断,以使信号立即发挥作用。


操作”的粒度尚未陈述,但是睡眠显然是不间断的,因此信号处理程序仅在完成后才运行。有了不同的作业,可以用分离而不是JOIN中断

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

,它仍然会在正确的时间停止该循环,但我没有迹象表明信号处理程序运行。 (在我的测试中,我有信号处理程序还编写一个文件,并使用分离否。)它对我来说都可以使用共享变量等,就像在问题中一样。

这个睡眠当然并不重要 - 但是,请仔细测试信号旨在停止的实际作业。

These "signals" aren't the actual OS signals, and there are operations they won't interrupt

CAVEAT: The thread signalling capability provided by this module does not actually send signals via the OS. It emulates signals at the Perl-level such that signal handlers are called in the appropriate thread. For example, sending $thr->kill('STOP') does not actually suspend a thread (or the whole process), but does cause a $SIG{'STOP'} handler to be called in that thread (as illustrated above).
...
Correspondingly, sending a signal to a thread does not disrupt the operation the thread is currently working on: The signal will be acted upon after the current operation has completed. For instance, if the thread is stuck on an I/O call, sending it a signal will not cause the I/O call to be interrupted such that the signal is acted up immediately.

The granularity of what the "operation" is isn't stated but sleep is clearly uninterruptable so the signal handler runs only after it completes. With a different job to interrupt

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;

Output

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

With detach instead of join it still stops that loop at the right time but I see no indication that a signal handler ran. (In my tests I have the signal handler also write a file and with detach it doesn't.) It all works the same for me with a shared variable etc, like in the question.

This sleep doesn't matter of course -- but it is all a warning to carefully test with actual jobs that the signal is aimed to stop.

尘世孤行 2025-02-20 18:49:07

信号只能发送到进程。因此,$ thread-&gt; kill('stop')不可能发送实际信号。因此,没有任何中断睡眠

在每个语句之间,Perl检查是否有“信号”。如果有信号,则可以处理。因此,仅一旦睡眠完成才能完成“信号”。

如果您有十秒钟的睡眠,而不是十秒钟的睡眠,那么等待最多将是一秒钟。

Signals can only be sent to processes. As such, $thread->kill('STOP') can't possibly be sending an actual signal. As such, nothing interrupts sleep.

Between each statement, Perl checks if a "signal" came in. If it has, it handles it. So the "signal" is only handled once sleep completes.

If you had ten one second sleeps instead of one ten second sleep, the wait would be at most one second.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文