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

请求取消时在同一线程内执行代码的实现方案

解决方案

核心思路是确保线程在取消触发或任务结束时,能在自身线程上下文内执行电源状态重置逻辑,避免while轮询。以下是两种可靠实现方式:

方式一:利用Rx的Finally操作符(适配你的Observable场景)

Rx的Finally操作符会在序列完成、报错或被取消时自动执行收尾逻辑,且能保证在调度器绑定的线程上运行(即你的目标线程):

private static void RunJob()
{
    _tokenSource.Token.ThrowIfCancellationRequested();

    bool success = false;
    try
    {
        success = SetUpThreadState();

        if (success)
        {
            Observable.Timer(expireAt, Scheduler.CurrentThread)
                // 不管Timer触发还是被取消,都会执行重置
                .Finally(() => ResetThreadState())
                .Subscribe(
                    t => CancelExistingThread(),
                    _tokenSource.Token);
        }
    }
    catch (OperationCanceledException ex)
    {
        Log($"Background thread termination. Message: {ex.Message}");
        // 处理订阅前就触发取消的场景
        if (success) ResetThreadState();
    }
    finally
    {
        // 处理方法正常退出的场景
        if (success) ResetThreadState();
    }
}

private static void ResetThreadState()
{
    // 重置线程电源状态到默认
    SetThreadExecutionState(ES_CONTINUOUS);
}

方式二:用等待句柄阻塞线程,取消时唤醒执行重置

如果需要更直接控制线程生命周期,可借助ManualResetEvent让线程等待取消信号,触发后在自身上下文执行重置:

private static void RunJob()
{
    _tokenSource.Token.ThrowIfCancellationRequested();

    bool success = false;
    using var cancellationEvent = new ManualResetEvent(false);
    try
    {
        // 注册取消回调,触发时唤醒线程
        using var _ = _tokenSource.Token.Register(() => cancellationEvent.Set());

        success = SetUpThreadState();

        if (success)
        {
            var subscription = Observable.Timer(expireAt, Scheduler.CurrentThread)
                .Subscribe(
                    t => 
                    {
                        CancelExistingThread();
                        cancellationEvent.Set(); // Timer触发后唤醒线程
                    },
                    _tokenSource.Token);

            // 等待取消或Timer触发,线程在此阻塞
            WaitHandle.WaitAny(new[] { cancellationEvent, _tokenSource.Token.WaitHandle });

            subscription.Dispose(); // 清理Rx订阅
        }
    }
    catch (OperationCanceledException ex)
    {
        Log($"Background thread termination. Message: {ex.Message}");
    }
    finally
    {
        // 最终在目标线程执行状态重置
        if (success) ResetThreadState();
    }
}

private static void ResetThreadState()
{
    SetThreadExecutionState(ES_CONTINUOUS);
}

关键注意事项

  • SetThreadExecutionState必须在目标线程调用,上述两种方式均保证重置逻辑在RunJob所在线程执行;
  • 确保SetUpThreadState中使用正确的参数(如ES_CONTINUOUS | ES_SYSTEM_REQUIRED),重置时仅传递ES_CONTINUOUS即可恢复默认电源管理;
  • 若使用Rx方案,需确认Scheduler.CurrentThread确实绑定到目标线程,避免跨线程执行重置逻辑。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 19:45:12