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

如何在单线程中通过信号量实现类上下文切换,完成Windows平台下基于Zephyr的nRF5340嵌入式固件单线程多实例仿真

当然有可行的方案!你要的本质就是单线程下的协作式多任务机制——完全能实现函数在信号量处暂停,等其他逻辑释放后再继续执行,还能轻松创建多个独立的设备实例。下面给你几个实用的实现思路和代码示例:

方案1:用C++20协程(最优雅的现代实现)

C++20原生支持的协程天生适配你的需求:它允许函数主动挂起,之后在任意时机恢复执行,状态由语言自动维护。你可以给每个设备实例封装成一个协程,配合自定义的用户态信号量实现同步。

第一步:实现可挂起的信号量

#include <coroutine>
#include <queue>
#include <atomic>

class Semaphore {
public:
    explicit Semaphore(int initial_count = 0) : count_(initial_count) {}

    // 定义等待信号量的可等待对象(协程挂起点)
    struct Awaiter {
        Semaphore& sem;
        // 检查信号量是否可用,可用则直接执行,不用挂起
        bool await_ready() const noexcept { return sem.count_ > 0; }
        // 协程挂起时,把当前协程句柄加入等待队列
        void await_suspend(std::coroutine_handle<> h) noexcept {
            sem.waiters_.push(h);
        }
        // 协程恢复时,消耗一个信号量计数
        void await_resume() noexcept {
            --sem.count_;
        }
    };

    // 供协程调用的等待接口
    Awaiter wait() noexcept {
        return Awaiter{*this};
    }

    // 释放信号量,唤醒一个等待的协程
    void release() noexcept {
        ++count_;
        if (!waiters_.empty()) {
            auto h = waiters_.front();
            waiters_.pop();
            h.resume(); // 恢复协程执行
        }
    }

private:
    std::atomic<int> count_;
    std::queue<std::coroutine_handle<>> waiters_;
};

第二步:封装设备实例的协程任务

#include <iostream>

struct Device {
    Semaphore sem;
    int id;

    explicit Device(int device_id) : id(device_id) {}

    // 定义协程返回类型
    struct Task {
        struct promise_type {
            Task get_return_object() noexcept { return {}; }
            std::suspend_never initial_suspend() noexcept { return {}; }
            std::suspend_never final_suspend() noexcept { return {}; }
            void return_void() noexcept {}
            void unhandled_exception() noexcept {}
        };
    };

    // 设备的主任务协程
    Task run() {
        std::cout << "Device " << id << ": 启动任务,等待信号量\n";
        co_await sem.wait(); // 在这里挂起,直到信号量被释放
        std::cout << "Device " << id << ": 信号量已获取,继续执行后续逻辑\n";
        // 这里可以添加原固件中的任务逻辑
    }
};

第三步:单线程调度多个设备实例

int main() {
    Device dev1(1);
    Device dev2(2);

    // 启动两个设备的任务(dev1、dev2会在wait处挂起)
    dev1.run();
    dev2.run();

    std::cout << "主线程:释放设备1的信号量\n";
    dev1.sem.release(); // 唤醒dev1的协程

    std::cout << "主线程:释放设备2的信号量\n";
    dev2.sem.release(); // 唤醒dev2的协程

    return 0;
}

这个方案的优势是代码简洁、逻辑清晰,每个设备实例的状态由协程自动管理,全程在单线程内执行,没有操作系统级线程切换的开销。

方案2:手动实现状态机(兼容C++11及更早版本)

如果你的开发环境不支持C++20,手动状态机是兼容性最好的选择。核心思路是把设备的任务逻辑拆分成多个状态,每次调用只执行当前状态对应的代码片段,遇到信号量等待时保存状态并返回,下次调用再从该状态继续。

第一步:实现用户态信号量

#include <atomic>

class Semaphore {
public:
    explicit Semaphore(int initial_count = 0) : count_(initial_count) {}

    // 尝试获取信号量,返回是否成功
    bool try_wait() noexcept {
        if (count_ > 0) {
            --count_;
            return true;
        }
        return false;
    }

    void release() noexcept {
        ++count_;
    }

private:
    std::atomic<int> count_;
};

第二步:封装带状态的设备实例

#include <iostream>
#include <vector>

enum class DeviceState {
    INIT,
    WAITING_FOR_SEM,
    RUNNING_AFTER_SEM,
    FINISHED
};

class Device {
public:
    explicit Device(int device_id) : id(device_id), state(DeviceState::INIT) {}

    // 每次调用执行一段逻辑,直到需要挂起或完成
    void step() {
        switch (state) {
            case DeviceState::INIT:
                std::cout << "Device " << id << ": 启动任务,尝试获取信号量\n";
                if (!sem.try_wait()) {
                    state = DeviceState::WAITING_FOR_SEM;
                    std::cout << "Device " << id << ": 信号量不可用,暂停执行\n";
                    return;
                }
                state = DeviceState::RUNNING_AFTER_SEM;
                // 状态流转,继续执行后续逻辑
            case DeviceState::RUNNING_AFTER_SEM:
                std::cout << "Device " << id << ": 信号量已获取,执行任务逻辑\n";
                // 这里添加原固件的任务代码
                state = DeviceState::FINISHED;
                break;
            case DeviceState::WAITING_FOR_SEM:
                if (sem.try_wait()) {
                    std::cout << "Device " << id << ": 信号量可用,恢复执行\n";
                    state = DeviceState::RUNNING_AFTER_SEM;
                } else {
                    // 信号量仍不可用,直接返回,下次再尝试
                    return;
                }
                break;
            case DeviceState::FINISHED:
                return;
        }
    }

    Semaphore sem;
    int id;
private:
    DeviceState state;
};

第三步:单线程轮询调度

int main() {
    std::vector<Device> devices = {Device(1), Device(2)};

    bool all_finished;
    do {
        all_finished = true;
        // 轮询每个设备的step函数,执行一段逻辑
        for (auto& dev : devices) {
            if (dev.state != DeviceState::FINISHED) {
                dev.step();
                all_finished = false;
            }
        }
        // 在这里处理信号量释放逻辑
        devices[0].sem.release();
        devices[1].sem.release();
    } while (!all_finished);

    return 0;
}

这个方案需要手动维护任务状态,代码稍显繁琐,但兼容性极强,不需要依赖任何C++20特性。

关键注意事项

  • 协作式调度:所有任务的暂停和恢复都是主动触发的,不存在抢占式切换,必须确保每个任务的step函数或协程不会无限执行,否则会阻塞整个线程。
  • 实例隔离:每个设备实例必须拥有独立的信号量和状态(协程句柄或状态变量),确保多个实例之间互不干扰。
  • 用户态同步:不需要使用操作系统提供的线程同步原语(比如std::semaphore),因为全程在单线程内执行,用户态实现足够安全高效。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.01 00:52:42