共享内存 - 需要同步

Shared memory - need for synchronization

本文关键字:同步 内存 共享      更新时间:2023-10-16

我见过一个项目,其中进程之间的通信是使用共享内存进行的(例如,在Windows下使用::CreateFileMapping),并且每次其中一个进程想要通知共享内存中某些数据可用时,使用命名事件的同步机制通知相关方共享内存的内容发生了变化。

我担心的是,读取新信息的进程不存在适当的内存围栏,以了解它必须使其数据副本无效,并在生产者进程"发布"数据后从主内存中读取数据。

您知道如何使用共享内存在Windows上完成此操作吗?

编辑只是想补充一点,在创建文件映射后,进程仅使用 MapViewOfFile() API 一次,并且对共享数据的每次新修改都使用初始调用 MapViewOfFile() 获得的指针来读取通过共享内存发送的新数据。正确的同步是否要求每次共享内存中的数据更改时,读取数据的进程每次都必须创建 MapViewOfFile() ?

如果使用 Windows 命名事件来发出更改信号,那么一切应该都正常。

进程 A 更改数据并调用SetEvent

进程 B 使用 WaitForSingleObject 或类似方式等待事件,并看到它已设置。

然后,进程 B 读取数据。 WaitForSingleObject包含所有必要的同步,以确保进程 B 读取进程 A 在调用 SetEvent 之前所做的更改。

当然,如果在调用 SetEvent 后对数据进行了任何更改,则当进程 B 读取数据时,这些更改可能会也可能不会显示。

如果您不想使用事件,则可以使用使用CreateMutex创建的互斥体,也可以使用InterlockedExchangeInterlockedIncrementInterlocked...函数编写无锁代码。

无论执行同步如何,都不需要多次调用MapViewOfFile

你在Windows上寻找共享内存的是InterlockedExchange函数。 请参阅此处的 msdn 文章。 引用了真正重要的部分:

此函数生成完整的内存屏障(或围栏)以确保 内存操作按顺序完成。

这将跨进程运行。 我以前使用过它,发现它在共享内存之上实现类似互斥体的结构是 100% 可靠的。

如何做到这一点是你用"set"值交换它。 如果你被"清除"回来,你就拥有了它(这是清楚的),但如果你被"设置"回来,那么其他人就有了。 你循环,在循环之间睡觉,等等,直到你"得到"它。 基本上是这样的:

#define LOCK_SET 1
#define LOCK_CLEAR 0
int* lock_location = LOCK_LOCATION; // ensure this is in shared memory
if (InterlockedExchange(lock_location, LOCK_SET) == LOCK_CLEAR)
{
    return true; // got the lock
}
else
{
    return false; // didn't get the lock
}

如上所述,循环直到你"得到"它。

我们将进程 A 称为数据生产者,将进程 B 称为数据使用者。到目前为止,流程 A 有一种机制来通知进程 B 已生成新数据。我建议您创建一个反向通知(从 B 到 A),告诉进程 A 数据已被消耗。如果出于性能原因,您不希望进程 A 等待数据被使用,则可以在共享内存中设置环形缓冲区。