在单线程中使用信号量模拟多线程

Simulate Multithreading with semaphores in a single thread

我想在 Windows 机器 (C++) 上模拟嵌入式设备的固件 (C++)。 微控制器 (nRF5340) 上的固件 运行s 和作为操作系统的 运行s Zephyr。 在真正的固件中有多个任务。

现在的挑战是:我希望能够为一个虚拟设备创建多个实例,但每个设备只能 运行 在一个线程中。

信号量用于多线程(在固件中),被阻塞或释放。有没有一种方法可以在单个线程中使用信号量并实现一种上下文切换?

因此,例如,一个函数被调用 运行 一直到信号量。当达到信号量时,可以调用一个新函数来释放前一个信号量,以便原始函数可以继续(但都在一个线程中)。

这可以通过协程来完成。协程就像一个函数,但在函数体的某个位置程序员调用了 yield。这会保存协程的状态。稍后可以在屈服点恢复。

协程已添加到 C++20。但是,他们还没有为任务管理库添加规范。自己实施将需要大量工作。你可以找到第三方的。 Boost 还有 3 个早于 C++20 的协程库(1 个旧的,1 个当前的,1 个专门用于 Asio)。使用第 3 方任务管理库可能是更好的方法,但您将不得不使用 C++20 学习一个可能很快就会过时的新库。

或者,如果您允许每个虚拟设备为其每个模拟线程生成一个实际线程,然后编写某种同步程序,以便每个虚拟设备在任何给定时间仅运行一个线程,则您可以进行变通。您实质上是在利用真正的线程内置上下文切换这一事实。我会使用这种方法,因为它避免了对与您的项目无关的事情进行大量开发,而且我已经知道如何使用线程工具。

最后,您可以为每个模拟线程创建一个“堆栈”并自己进行上下文切换。你会失去便携性。基本上您是在编写自己的协程库,但它是根据您的用例量身定制的,因此使用起来会更容易。但是,开发时间可能会与学习如何使用现有库相抵消。

回到中间方法(每个模拟线程一个真实的线程)......你的屈服函数看起来像:

每个模拟线程都有自己的信号量(因此您可以指定调用哪个):

void switch_to(T& other)
{
    other.up()
    me.down();
}

或者虚拟设备上的所有模拟线程共享一个信号量:

void switch()
{
    sem.up();
    sem.down();
}

(此方法仅在信号量实现公平性时有效。)

这两种方式,您都以 0 次传递初始化信号量,并让每个线程调用信号量上的 down() 作为其第一个操作(这将每个线程初始化为阻塞状态)。

C++ 没有标准的信号量,但您可以使用条件变量实现一个。