如何解决根据 C++11语言/库标准,线程安全函数的定义是什么?
TL;DR:说特定函数是“线程安全的”是什么意思,因为同时调用两个可能不同的发生数据争用 功能?这个问题在人们告诉“const 意味着/暗示 C++11 中的线程安全”的上下文中特别相关[1][2]
考虑以下示例:
class X {
int x,y; // are some more complex type (not supported by `std::atomic`)
std::mutex m;
public:
void set_x (int new_x) {x = new_x;} // no mutex
void get_x () const {return x;}
void set_y (int new_y) {
std::lock_guard<std::mutex> guard(m); // guard setter with mutex
y = new_y;
}
void get_y () const {return y;}
}
set_x
线程安全吗?
当然,set_x
不是线程安全的,因为从两个线程同时调用它会导致数据竞争。
get_x
、get_y
和 set_y
线程安全吗?
存在两种可能的原因:
- 是的,它们是线程安全的,因为同时从两个线程调用
get_x
/get_y
/set_y
不会导致数据竞争。 - 不,它们不是线程安全的,因为同时从两个线程调用
get_x
(或get_y
)和set_x
(或set_y
)会导致数据竞争。
问题总结
哪个推理是正确的?
- 一个函数是线程安全的,如果同时从两个线程调用它不会导致数据竞争。可以用于
set_x
/get_x
,但不能用于set_y
/get_y
,因为这将导致set_y
和get_y
是线程安全,但类Y
不像同时从两个线程调用set_y
和get_y
导致数据竞争。 - 一个函数是线程安全的,如果它不访问任何可以被另一个函数在没有内部同步的情况下修改的内存。在我看来,这似乎是最一致的选项,但不是经常使用的方式(请参阅相关主题)。
相关主题
请注意,我已阅读以下相关主题:
- Does const mean thread-safe in C++11? ['mean' = 你有责任做到]
- How do I make a function thread safe in C++?
- https://isocpp.org/blog/2012/12/you-dont-know-const-and-mutable-herb-sutter
- https://softwareengineering.stackexchange.com/questions/379516/is-the-meaning-of-const-still-thread-safe-in-c11
解决方法
C++ 标准不使用诸如“线程安全”之类的术语;它使用更具体的语言。人类使用“线程安全”之类的术语是因为我们发现它们很有用。
线程安全函数的常见想法是,在调用时,假设没有其他人搞砸,不会造成数据竞争。 get_x
是线程安全的;在所有条件相同的情况下,您可以从任意数量的线程调用它并获得合理的结果。即使它不能与 set_x
同时调用也是如此,因为这会导致数据竞争。但数据竞争的原因是您调用了非线程安全函数:set_x
。
将函数归类为“线程安全”与否的重点是归咎于责任。如果您只调用“线程安全”函数,那么您的代码就是“线程安全”的。如果您偏离了“线程安全”函数的边界,那么正是您偏离了这些边界,导致了数据竞争。
并发 get_x
调用导致数据竞争不是 set_x
的错。
对于 get/set_y
的问题,如前所述,“线程安全”不是一个计算性术语,也不是一个严格的标准术语。这是一个人类术语,是对计算现实的简化。
什么是“线程安全”的规则基本上是“您可以与任何其他线程安全函数同时调用任何线程安全函数”。如果您不能同时调用 get_y
和 set_y
,则它们不是“线程安全的”。
从严格的角度来看,准确描述这两个函数的方式是set_y
与同一对象上其他对set_y
的调用同步,get_y
与其他对{的调用同步{1}} 在同一个对象上。我们并没有说它们彼此同步这一事实告诉您您需要知道什么。
从简化的角度来看,get_y
是“线程安全的”; set_y
不是。但您也可以说 get_y
是“线程安全的”而 get_y
不是。这并不重要,因为它只是一种简化。
并且您是否声明 set_y
get_y
并不是使它“线程安全”的原因。 Sutter 说,如果你编写一个 const
函数,你的工作就是以一种“线程安全”的方式来完成它。因此,const
被破坏,因为它不是以线程安全的方式编写的,因为它不能被其他线程安全函数以线程安全的方式调用。
我也同意你的以下声明:
2. 如果一个函数不访问任何可以被另一个函数在没有内部同步的情况下修改的内存,那么它就是线程安全的。在我看来,这似乎是最一致的选择,但不是经常使用的方式。
只要共享变量是原子的,并且正确使用互斥锁来实现同步,我看你上面的说法没有任何问题。
,请注意,这是基于我自己的研究并提供其他人的意见的我自己的观点。
线程安全函数的定义是什么?
一个函数是线程安全的,如果它不访问(读或写)任何可以被另一个函数在没有内部同步的情况下修改的内存:只有 set_y
是线程安全的。
请注意,C++ 标准并未明确定义线程安全,该标准使用术语数据竞争。有关更多信息,请参阅 Nicol Bolas 的回答:线程安全并不总是非黑即白。
常量函数意味着线程安全按位常量或内部同步
术语线程安全在“const 函数意味着线程安全”的上下文中被滥用。
“const 函数意味着线程安全”的意思是从多个线程调用 const 函数应该是安全的(不调用非const 函数同时在另一个线程中)。
正如 Herb Sutter (29:43) 自己所说,在这种情况下,线程安全意味着按位常量 或 内部同步,如果其他非可以同时调用 const 函数。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。