放宽内存顺序的原子加载和存储
Atomic load and store with memory order relaxed
在我读到的所有地方,我都看到强烈建议不要使用宽松的内存顺序,我想知道下面的代码片段是否是这种例外情况之一,它可以在其中工作,或者有任何我没有看到的后果。
class SessionHolder {
public:
void set_session(std::shared_ptr<Session> session) {
std::atomic_store_explicit(&_session, session, std::memory_order_relaxed);
}
std::shared_ptr<Session> get_session() const {
return std::atomic_load_explicit(&_session, std::memory_order_relaxed);
}
private:
std::shared_ptr<Session> _session;
};
原则上,当我执行 get_session
时,我不关心得到的是哪个会话,只要它是会话或 nullptr。但是,如果另一个线程进行存储(这种情况很少发生),我希望最终能在合理的延迟内获得该值。
- 据我所知,甚至不能保证我会得到那个值,只是我会得到一个在某个时间点存储在那里的值,但我总能得到 nullptr。
- 在实践中似乎可行,我可以预期它会在某些特定的 situations/platform 中失败(总是检索 nullptr)吗?
- 我可以调整存储操作中的内存顺序来解决这个问题吗?例如
memory_order_release
会将更改传播到另一个线程?
有关如何使用此 class 的更多上下文:
基本上,我有某种不断产生数据的流。在某个时刻,客户端进程可能会连接(启动会话)并开始收听此数据。生产者线程不断写入流,如果没有活动会话,数据将被丢弃,否则发送给客户端。另一个线程,在某个点(不经常),当客户端连接时到达此流并设置会话。
生产者线程必须有尽可能少的争用,即使这意味着丢失一些消息。
As I understand, it is not even guaranteed that I would get that value, just that I will get a value that was stored there at some point in time, but I could always get nullptr.
是的,假设每次调用 get_session
都会 return 一个空指针,但可能性不大。即使是宽松的内存顺序也能提供的一种保证是,一旦特定线程观察到一个值,同一线程就不能随后观察到先前的值。因此,一旦特定线程开始观察到非空指针,该线程将在后续调用 get_session
时始终观察到非空指针(假设从未存储空指针)。
In practice seems to work, can I expect it to fail (always retrieving nullptr) in some situations/platform specifically?
我不知道。
Can I adjust the memory order in the store operation only to fix this? e.g. memory_order_release
would spread the changes to the other thread?
没有。该标准不提供任何方法来确保线程间通信在任何特定时间内完成。只有执行才能提供这样的保证。
最后一点:nullptr
与 "a null pointer" 不同。它是一个独特类型的特殊常量,可以转换为另一种类型的空指针。
在我读到的所有地方,我都看到强烈建议不要使用宽松的内存顺序,我想知道下面的代码片段是否是这种例外情况之一,它可以在其中工作,或者有任何我没有看到的后果。
class SessionHolder {
public:
void set_session(std::shared_ptr<Session> session) {
std::atomic_store_explicit(&_session, session, std::memory_order_relaxed);
}
std::shared_ptr<Session> get_session() const {
return std::atomic_load_explicit(&_session, std::memory_order_relaxed);
}
private:
std::shared_ptr<Session> _session;
};
原则上,当我执行 get_session
时,我不关心得到的是哪个会话,只要它是会话或 nullptr。但是,如果另一个线程进行存储(这种情况很少发生),我希望最终能在合理的延迟内获得该值。
- 据我所知,甚至不能保证我会得到那个值,只是我会得到一个在某个时间点存储在那里的值,但我总能得到 nullptr。
- 在实践中似乎可行,我可以预期它会在某些特定的 situations/platform 中失败(总是检索 nullptr)吗?
- 我可以调整存储操作中的内存顺序来解决这个问题吗?例如
memory_order_release
会将更改传播到另一个线程?
有关如何使用此 class 的更多上下文:
基本上,我有某种不断产生数据的流。在某个时刻,客户端进程可能会连接(启动会话)并开始收听此数据。生产者线程不断写入流,如果没有活动会话,数据将被丢弃,否则发送给客户端。另一个线程,在某个点(不经常),当客户端连接时到达此流并设置会话。
生产者线程必须有尽可能少的争用,即使这意味着丢失一些消息。
As I understand, it is not even guaranteed that I would get that value, just that I will get a value that was stored there at some point in time, but I could always get nullptr.
是的,假设每次调用 get_session
都会 return 一个空指针,但可能性不大。即使是宽松的内存顺序也能提供的一种保证是,一旦特定线程观察到一个值,同一线程就不能随后观察到先前的值。因此,一旦特定线程开始观察到非空指针,该线程将在后续调用 get_session
时始终观察到非空指针(假设从未存储空指针)。
In practice seems to work, can I expect it to fail (always retrieving nullptr) in some situations/platform specifically?
我不知道。
Can I adjust the memory order in the store operation only to fix this? e.g.
memory_order_release
would spread the changes to the other thread?
没有。该标准不提供任何方法来确保线程间通信在任何特定时间内完成。只有执行才能提供这样的保证。
最后一点:nullptr
与 "a null pointer" 不同。它是一个独特类型的特殊常量,可以转换为另一种类型的空指针。