对 std::atomic::load 的结果使用 Structure dereference(->) 运算符是否安全

lau*_*svr 5 c++ atomic c++11 stdatomic

在尝试使用 std 原子指针时,我遇到了以下问题。说我这样做:

std::atomic<std::string*> myString;
// <do fancy stuff with the string... also on other threads>

//A can I do this?
myString.load()->size()

//B can I do this?
char myFifthChar = *(myString.load()->c_str() + 5);

//C can I do this?
char myCharArray[255];
strcpy(myCharArray, myString.load()->c_str());
Run Code Online (Sandbox Code Playgroud)

我很确定 C 是非法的,因为在此期间 myString 可能会被删除。

但是我不确定 A 和 B。我认为它们是非法的,因为在执行读取操作时可能会尊重指针。

但是,如果是这种情况,您怎么能从可能被删除的原子指针中读取数据。由于负载为1步,读取数据为1步。

Arn*_*gel 3

有人提到你的做法是有风险的。您可能需要考虑以下内容:std::shared_ptr<const std::string>与不可变值以及shared_ptr atomic_load和atomic_store一起使用。std::shared_ptr将确保您不会访问悬空指针,而不变性(字符串在构造后不会更改)将保证对字符串本身的访问是线程安全的,因为标准定义的所有const方法都是线程安全的。

编辑:根据要求解释“风险业务”的含义:如果您使用std::atomic<std::string *>,那么很容易意外引入竞争条件,例如

// Data
std::atomic<std::string *> str(new std::string("foo"));

// Thread 1
std::cout << *str.load();

// Thread 2
*str.load() = "bar"; // race condition with read access in thread 1

// Thread 2 (another attempt using immutable instances)
auto newStr = new std::string("bar");
auto oldStr = str.exchange(newStr);
delete oldStr;  /* race condition with read access in thread 1
                   because thread 1 may have performed load() before
                   the exchange became visible to it, and may not
                   be finished using the old object. */
Run Code Online (Sandbox Code Playgroud)

请注意,这与 无关operator <<,即使只是调用size()线程 1 中的字符串也会导致竞争条件。

在实践中,人们可能会看到“修复”,例如在更新sleep之前添加一个delete不可变字符串,以便线程 1 有足够的时间使用旧指针完成其业务。尽管这可能在特定实现中的大部分时间都有效,但它没有引入真正的排序(在 C++ 标准中是发生在之前的关系),因此不是正确的 rsp。便携式解决方案。