我把这段代码写成了测试:
#include <iostream>
#include <thread>
#include <mutex>
int counter = 0;
auto inc(int a) {
for (int k = 0; k < a; ++k)
++counter;
}
int main() {
auto a = std::thread{ inc, 100000 };
auto b = std::thread{ inc, 100000 };
a.join();
b.join();
std::cout << counter;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
该counter变量是全球性的,因此,创建2个线程a和b,我希望找到一个数据的比赛.输出为200000而不是随机数.为什么?
此代码是一个固定版本,它使用一个mutex全局变量只能访问一次(每次1个线程).结果仍然是200000.
std::mutex mutex;
auto inc(int a) {
mutex.lock();
for (int k = 0; k < a; ++k)
++counter;
mutex.unlock();
} …Run Code Online (Sandbox Code Playgroud) 在此示例程序中,我试图避免利用lambda函数(称为data_race)来使用前向声明和循环依赖关系
struct B{
int x;
std::thread* tid;
B(int _x){
x = _x;
tid = NULL;
}
~B(){
if(tid != NULL) delete tid;
}
void run(std::function<void(B*)> f){
auto body = [&f, this](){
f(this);
};
tid=new std::thread(body);
}
void wait(){
tid->join();
}
};
struct A{
int x;
std::mutex mtx;
A(int _x){
x = _x;
}
void foo(B* b){
std::unique_lock<std::mutex> lock(mtx);
x = b->x;
};
};
int main(){
A a(99);
std::vector<B> v;
auto data_race = [&a](B* b){ a.foo(b);};
for(int i=0; i<10; i++){
v.push_back(B(i)); …Run Code Online (Sandbox Code Playgroud) 我试图了解数据竞赛和无日期竞赛之间的界限在哪里,以及有关未定义行为的后果是什么。
考虑这个例子:
#include <chrono>
#include <thread>
#include <cstdlib>
#include <iostream>
#include <ctime>
#include <functional>
void write(int delay, int& value, int target) {
std::this_thread::sleep_for(std::chrono::milliseconds(delay));
value = target;
}
int main() {
int x;
std::srand(std::time(nullptr));
auto t1 = std::thread(write, rand()%100, std::ref(x), 42);
auto t2 = std::thread(write, rand()%100, std::ref(x), 24);
t1.join();
t2.join();
std::cout << x;
}
Run Code Online (Sandbox Code Playgroud)
这段代码是否总是有数据竞争,还是只是有时?上面代码的行为是否总是根据标准未定义,或者只是有时(取决于 的结果rand())?
PS:当然,我不知道输出是否会是42或24,但是在存在未定义行为的情况下,我什至不会肯定地期望两者中的任何一个,它可能是123或"your cat ate my fish"。
PPS:我不关心高质量的随机性,因此rand()对于这个例子来说很好。
有人告诉我memCacheInstance有竞争条件,但go run -race不知道。
代码:
type MemCache struct {
data []string
}
var memCacheInstance *MemCache
var memCacheCreateMutex sync.Mutex
func GetMemCache() *MemCache {
if memCacheInstance == nil {
memCacheCreateMutex.Lock()
defer memCacheCreateMutex.Unlock()
if memCacheInstance == nil {
memCacheInstance = &MemCache{
data: make([]string, 0),
}
}
}
return memCacheInstance
}
Run Code Online (Sandbox Code Playgroud) 最近,我发现一些代码如下所示:
var m map[int]int
func writem() {
tmpm := make(map[int]int)
for i := 0; i < 4000000; i++ {
tmpm[i] = i + 10
}
m = tmpm
}
func readm() {
for k, v := range m {
_, _ = k, v
}
}
func main() {
writem()
go readm()
writem()
}
Run Code Online (Sandbox Code Playgroud)
该程序运行良好,但是我认为writem可以通过m = tmpm在for循环之前移动功能主体来重新排序,因为这不会更改此goroutine中的行为。而且这种重新排序将导致concurrent map read and map write问题。正如Go Memory Model所说:
仅当重新排序不会改变语言规范所定义的该goroutine中的行为时,编译器和处理器才可以对单个goroutine中执行的读写进行重新排序。
是的,还是这样编写代码安全吗?