首页 > 解决方案 > 在单个线程中使用信号量模拟多线程

问题描述

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

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

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

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

标签: c++multithreadingsimulationsemaphore

解决方案


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

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

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

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

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

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

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

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

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

(这种方法只有在信号量实现公平的情况下才有效。)

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

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


推荐阅读