我遇到一个问题,主线程中的函数被阻塞,直到在另一个线程中设置了局部变量。我使用信号量阻止主线程执行:
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的情况怎么样? (请不要介意阵列的大小)
答案 0 :(得分:2)
结果缓冲区中的数据很好。在lambda中调用memcpy可确保在信号量waitForSignal发出信号之前完整复制数据。但...
volatile
仅影响代码的优化方式。 data
并让lambda直接填充缓冲区?这样可以保存副本。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衍生产品上,你根本不需要做任何事情。在更疯狂的平台上,这通常由内存栅栏处理,无论如何你都会使用它。