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

时间:2017-07-20 02:31:19

标签: c++ multithreading

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

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的情况怎么样? (请不要介意阵列的大小)

2 个答案:

答案 0 :(得分:2)

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

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

答案 1 :(得分:2)

对于多线程代码,

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衍生产品上,你根本不需要做任何事情。在更疯狂的平台上,这通常由内存栅栏处理,无论如何你都会使用它。