多线程环境中C++内存访问

Memory access in C++ multithreading environment

本文关键字:内存 访问 C++ 环境 多线程      更新时间:2023-10-16

我有一个问题,即主线程中的函数被阻塞,直到在另一个线程中设置局部变量。我使用信号量来阻止主线程执行:

int sendRequest(Message request, void *data)
{
semaphore waitForReply;
volatile int result = 0;
volatile int retData[100];
client->sendRequest(request, [&result, &retData](const Message& reply){ // Callback is called from a different thread
result = reply.result;
memcpy(retData, reply.retData, sizeof(retData));
waitForReply.signal();
})
waitForReply.wait();
//At this line we want result var to be updated.
memcpy(data, retData, sizeof(retData));
return result;
}

问题是使用 volatile int 结果是否保证返回的结果是从回调接收的实际值? 这是解决此问题的最佳方法还是使用普通变量和互斥锁更好?

数组 retData 的情况如何?(请不要介意数组的大小(

Volatile

在C 或 C++ 中对多线程代码没有用,因为它不会创建内存屏障。但是,您的代码中没有任何内容需要内存屏障,因此您已经很好了。

错误代码的一个示例是:

// initially
my_pointer = nullptr;
ready_flag = false;
// thread 1
my_pointer = some_pointer_here;
ready_flag = true;
// thread 2
while (!ready_flag) /* wait */;
my_pointer->foo = bar;

这段代码很危险,因为很多事情可以使写入ready_flag在写入my_pointer之前变得可见,即使它在源代码中排在第二位。在这种情况下,您可以在为my_pointer分配值之前访问它。

内存屏障是一种指令,用于强制执行内存写入何时可见的特定顺序。例如,该代码需要在写入my_pointer和写入ready_flag之间建立内存屏障。C++标准库为此提供了std::atomic_thread_fence。此外,std::atomic类型都可能生成内存障碍。

在您的情况下,您的变量没有相互依赖关系(除了整个操作必须已完成(,并且信号量的wait方法几乎肯定存在障碍。

在内核/CPU 之间实现缓存和内存一致性的确切方法取决于平台。AFAIK,在x86和ARM衍生产品上,你根本不需要做任何事情。在更疯狂的平台上,这通常由内存围栏处理,无论如何您都会使用它。

生成的缓冲区中的数据很好。在 lambda 中调用 memcpy 可确保在信号量 waitForSignal 发出信号之前完整复制数据。 但。。。

  1. 不,在这种情况下,volatile 关键字不执行任何操作。 MEMCPY 不受易失性关键字的影响。volatile仅影响代码的优化方式。
  2. 为什么不捕获data并让 lambda 直接填充该缓冲区? 这将保存副本。
  3. 我没有看到任何参数来说明输出缓冲区的大小data. 这不是很好的做法。 我建议你考虑使用 std::vector 或其他一些安全的方式来实现缓冲区。