CP.2: Avoid data races
CP.2:避免数据竞争
Reason(原因)
Unless you do, nothing is guaranteed to work and subtle errors will persist.
除非你做到了,否则没有任何东西可以保证动作,微妙的错误还会继续存在。
Note(注意)
In a nutshell, if two threads can access the same object concurrently (without synchronization), and at least one is a writer (performing a non-const operation), you have a data race. For further information of how to use synchronization well to eliminate data races, please consult a good book about concurrency.
简而言之,如果两个线程可以(不进行任何同步)并发访问同一个对象,至少一个线程执行写操作(执行非常量操作),就会发生数据竞争。为了获得如何更好地使用同步以消除数据竞争的进一步信息,请查阅有关并发的经典书籍。
Example, bad(反面示例)
There are many examples of data races that exist, some of which are running in production software at this very moment. One very simple example:
有关数据竞争的例子非常多,有些就发生于正在运行的产品级软件。下面是很简单的例子:
int get_id()
{
static int id = 1;
return id++;
}
The increment here is an example of a data race. This can go wrong in many ways, including:
代码中的增量操作就是数据竞争的例子。出错的方式可以有很多种,包括:
-
Thread A loads the value of id, the OS context switches A out for some period, during which other threads create hundreds of IDs. Thread A is then allowed to run again, and id is written back to that location as A's read of id plus one.
-
线程A获取id的值之后操作系统上下文从A中退出一段时间,这时另外的线程生成了几百个ID。接着线程A继续运行,这时id重新被写入,而值是A读取的局部变量加1之后的结果。
-
Thread A and B load id and increment it simultaneously. They both get the same ID.
-
线程A和B同时获取id并加1。它们得到同样的ID。
Local static variables are a common source of data races.
局部静态变量是数据竞争的常见来源。
Example, bad(反面示例):
void f(fstream& fs, regex pattern)
{
array<double, max> buf;
int sz = read_vec(fs, buf, max); // read from fs into buf
gsl::span<double> s {buf};
// ...
auto h1 = async([&] { sort(std::execution::par, s); }); // spawn a task to sort
// ...
auto h2 = async([&] { return find_all(buf, sz, pattern); }); // spawn a task to find matches
// ...
}
Here, we have a (nasty) data race on the elements of buf (sort will both read and write). All data races are nasty. Here, we managed to get a data race on data on the stack. Not all data races are as easy to spot as this one.
这里,保存在buf中的元素会发生(严重的)数据竞争(排序既包含读操作也包含写操作)。没有哪个数据竞争是不严重的。代码中的数据竞争发生在堆栈中的数据。不是所有的数据竞争都像本例这样容易被发现。
Example, bad(反面示例):
// code not controlled by a lock
unsigned val;
if (val < 5) {
// ... other thread can change val here ...
switch (val) {
case 0: // ...
case 1: // ...
case 2: // ...
case 3: // ...
case 4: // ...
}
}
Now, a compiler that does not know that val can change will most likely implement that switch using a jump table with five entries. Then, a val outside the [0..4] range will cause a jump to an address that could be anywhere in the program, and execution would proceed there. Really, "all bets are off" if you get a data race. Actually, it can be worse still: by looking at the generated code you may be able to determine where the stray jump will go for a given value; this can be a security risk.
现在,编译器不知道val会被修改,因此编译结果很可能是一个带有五个分支的跳转表。那么一旦val的值超越了范围[0..4],就有可能调转到程序的任何位置并从那里继续执行。真的,一旦发生了数据竞争,结果会怎么样谁也不知道。实际上,还有可能更坏:通过检查生成的代码,你或许可以准确算出针对一个给定的值,程序可以跳转到什么位置。这可能成为一个安全风险。
Enforcement(实施建议)
Some is possible, do at least something. There are commercial and open-source tools that try to address this problem, but be aware that solutions have costs and blind spots. Static tools often have many false positives and run-time tools often have a significant cost. We hope for better tools. Using multiple tools can catch more problems than a single one.
有一些是可能的,至少做点什么。有些商用和开源工具试图定位这些问题,但是需要注意的是:解决方案需要成本并存在盲区。静态工具会产生很多误报,而运行时工具通常需要巨大的成本。我们希望出现更好的工具。使用多种工具会比单一工具捕捉更多的错误。
There are other ways you can mitigate the chance of data races:
存在另外的方法可以降低数据竞争的可能性:
-
Avoid global data
-
避免全局数据
-
Avoid static variables
-
避免静态数据
-
More use of value types on the stack (and don't pass pointers around too much)
-
在堆栈上更多地使用值类型(并且不要来回传递指针)
-
More use of immutable data (literals, constexpr, and const)
-
更多地使用不可修改的数据(literals, constexpr, and const)
原文链接
https://github/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#cp2-avoid-data-races
新书介绍
以下是本人3月份出版的新书,拜托多多关注!
本书利用Python 的标准GUI 工具包tkinter,通过可执行的示例对23 个设计模式逐个进行说明。这样一方面可以使读者了解真实的软件开发工作中每个设计模式的运用场景和想要解决的问题;另一方面通过对这些问题的解决过程进行说明,让读者明白在编写代码时如何判断使用设计模式的利弊,并合理运用设计模式。
对设计模式感兴趣而且希望随学随用的读者通过本书可以快速跨越从理解到运用的门槛;希望学习Python GUI 编程的读者可以将本书中的示例作为设计和开发的参考;使用Python 语言进行图像分析、数据处理工作的读者可以直接以本书中的示例为基础,迅速构建自己的系统架构。
觉得本文有帮助?欢迎点赞并分享给更多的人。
阅读更多更新文章,请关注微信公众号【面向对象思考】
更多推荐
C++核心准则CP.2:避免数据竞争
发布评论