You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于Raku并发实现可停止简易定时器的技术咨询

如何在Raku中实现可停止的定时器?

你的原始代码核心问题在于:start方法里的无限循环直接阻塞了主线程,导致后续的stop和reset调用根本没机会执行,而且Promise也没真正发挥并发控制的作用。下面给出两种基于Raku并发特性的实现方案,彻底解决这个问题:

方案一:用Promise实现可停止定时器

这种方式通过后台线程执行定时器循环,用Promise作为停止信号,逻辑清晰且灵活:

class Timer {
    has Promise $!stop-promise;
    has Promise $!timer-promise;
    has Int $!counter is default(0);
    has Bool $!is-running is default(False);

    method start {
        return if $!is-running; # 避免重复启动
        $!is-running = True;
        $!stop-promise = Promise.new;
        
        # 用start块把循环放到后台线程,不阻塞主线程
        $!timer-promise = start {
            loop {
                # 同时等待1秒定时或停止信号,哪个先触发就执行哪个
                await Promise.anyof($!stop-promise, Promise.in(1));
                
                # 如果停止信号被触发,直接退出循环
                last if $!stop-promise.Broken;
                
                $!counter++;
                self.show;
            }
            $!is-running = False;
        }
    }

    method stop {
        return unless $!is-running;
        # 触发停止信号(若未触发过)
        $!stop-promise.break unless $!stop-promise.Broken;
        # 可选:等待后台线程完全结束,确保资源清理
        await $!timer-promise;
    }

    method reset {
        $!counter = 0;
        self.show;
    }

    method show {
        say "timer: $!counter";
    }
}

# 使用示例
my Timer $t .= new;
$t.start;
sleep 3; # 让定时器运行3秒
$t.stop;
$t.reset;

关键改动说明:

  • 用start关键字将定时器循环放到后台线程执行,主线程得以释放,能正常处理stop和reset调用。
  • 新增$!stop-promise作为停止信号,stop方法通过break触发该Promise,循环中通过Promise.anyof监听停止信号和1秒定时,收到停止信号就退出循环。
  • 加入$!is-running标志,防止重复启动定时器。

方案二:用Supply实现可停止定时器

Raku的Supply天生适合处理周期性事件,这种方式代码更简洁:

class Timer {
    has Supply $!timer-supply;
    has Supplier $!control-supplier;
    has Int $!counter is default(0);
    has Bool $!is-running is default(False);

    method start {
        return if $!is-running;
        $!is-running = True;
        
        # 创建控制信号源,用于发送停止指令
        $!control-supplier = Supplier.new;
        my $control-supply = $!control-supplier.Supply;
        
        # 创建每秒触发一次的事件流
        $!timer-supply = Supply.interval(1)
            .tap(
                # 每次触发时计数并显示
                -> $ {
                    $!counter++;
                    self.show;
                },
                # 事件流关闭时标记定时器停止
                done => { $!is-running = False }
            );
        
        # 监听控制信号,收到后关闭定时器事件流
        $control-supply.tap: -> $ { $!timer-supply.close }
    }

    method stop {
        return unless $!is-running;
        # 发送停止信号
        $!control-supplier.emit('stop');
    }

    method reset {
        $!counter = 0;
        self.show;
    }

    method show {
        say "timer: $!counter";
    }
}

# 使用示例
my Timer $t .= new;
$t.start;
sleep 3;
$t.stop;
$t.reset;

关键改动说明:

  • 用Supply.interval(1)生成每秒触发一次的事件流,通过tap处理计数和显示逻辑。
  • 创建control-supplier生成控制信号,stop方法通过emit发送停止信号,监听该信号的tap会关闭定时器Supply,终止事件流。

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 06:31:02