为什么这个多线程片段中存在竞争条件
why is there a race condition in this multithreading snippet
我在使用多线程的 C++ 中有这段代码,但我不确定为什么我得到了我得到的输出。
void Fun(int* var) {
int myID;
myID = *var;
std::cout << "Thread ID: " << myID << std::endl;
}
int main()
{
using ThreadVector = std::vector<std::thread>;
ThreadVector tv;
std::cout << std::thread::hardware_concurrency() << std::endl;
for (int i = 0; i < 3 ; ++i)
{
auto th = std::thread(&Fun, &i);
tv.push_back(std::move(th));
}
for (auto& elem : tv)
{
elem.join();
}
}
我想知道 i 变量是否存在竞争条件,如果存在,它是如何交错的?我试着编译它,我不断地得到线程 ID 打印输出 3,但我很惊讶,因为我认为变量必须是全局的才能被各种新线程访问?
这就是我认为会发生的事情:创建线程 1,Fun 在线程 1 中开始 运行,myid = 0,主线程继续 运行ning 并递增 i,第二个线程是创建的 myid 将是 myid=1... 等等。因此打印输出将是 myID 增量 i/e 1,2,3
我知道我可以用 std::lock_guard
解决这个问题,但我只是想知道交错 (LOAD、INCREMENT、STORE) 是如何发生的,这会导致 i
变量出现这种竞争条件。
感谢您的帮助,谢谢!
I am wondering if there is a race condition for the i variable
是的,绝对是。父线程写入 i
,这是一个非 atomic
变量,子线程读取它,没有任何干预同步。这就是 C++ 中数据竞争的确切定义。
and if so, how does it interleave?
C++ 中的数据竞争会导致未定义的行为,您可能观察到的任何行为都不必通过交错来解释。
I tried to compile it and I constantly got the Thread ID printout as 3, but I was surprised because I thought the variable had to be global in order to be accessed by the various new threads?
不,它不必是全局的。如果以某种方式将指针或引用传递给此类变量,则线程可以访问其他线程的本地变量。
This is what I thought would happen: thread 1 is created, Fun starts to run in thread 1 with myid = 0, main thread continues running and increments i, 2nd thread is created and the myid for that would be myid=1... and so on. And so the printout would be the myID in increments i/e 1,2,3
好吧,您的程序中没有任何内容会强制这些事件按该顺序发生(或变得可观察),因此实际上没有理由期望它们会发生。完全有可能,例如,三个线程都开始了,但直到 main
中的循环完成后才有机会实际 运行,此时 i
值为 3。(或者更确切地说,i
曾经所在的内存,因为它现在超出范围并且它的生命周期已经结束 - 这是一个单独的错误,您不能阻止它发生。)
这是不会出现数据竞争的代码版本:
#include <iostream>
#include <thread>
#include <vector>
// since `id` is passed by value, each thread will work on its own copy and no
// data race is possible
void fun(int id) { std::cout << "thread id: " << id << "\n"; }
int main() {
std::vector<std::thread> threads;
for (auto id = 0; id < 3; ++id) {
threads.emplace_back(fun, id);
}
for (auto& thread : threads) {
thread.join();
}
}
由于每个线程都接收到变量 id
的副本,因此不存在竞争(除了由于不同步 std::cout
而导致的混乱输出,但我认为这不是本次讨论的一部分)。
变量不需要是全局变量就可以在多线程中使用。事实上,全局变量常常使编写多线程代码变得更加困难,甚至几乎不可能,因为不能保证每次读取和写入都会适当同步。
我在使用多线程的 C++ 中有这段代码,但我不确定为什么我得到了我得到的输出。
void Fun(int* var) {
int myID;
myID = *var;
std::cout << "Thread ID: " << myID << std::endl;
}
int main()
{
using ThreadVector = std::vector<std::thread>;
ThreadVector tv;
std::cout << std::thread::hardware_concurrency() << std::endl;
for (int i = 0; i < 3 ; ++i)
{
auto th = std::thread(&Fun, &i);
tv.push_back(std::move(th));
}
for (auto& elem : tv)
{
elem.join();
}
}
我想知道 i 变量是否存在竞争条件,如果存在,它是如何交错的?我试着编译它,我不断地得到线程 ID 打印输出 3,但我很惊讶,因为我认为变量必须是全局的才能被各种新线程访问?
这就是我认为会发生的事情:创建线程 1,Fun 在线程 1 中开始 运行,myid = 0,主线程继续 运行ning 并递增 i,第二个线程是创建的 myid 将是 myid=1... 等等。因此打印输出将是 myID 增量 i/e 1,2,3
我知道我可以用 std::lock_guard
解决这个问题,但我只是想知道交错 (LOAD、INCREMENT、STORE) 是如何发生的,这会导致 i
变量出现这种竞争条件。
感谢您的帮助,谢谢!
I am wondering if there is a race condition for the i variable
是的,绝对是。父线程写入 i
,这是一个非 atomic
变量,子线程读取它,没有任何干预同步。这就是 C++ 中数据竞争的确切定义。
and if so, how does it interleave?
C++ 中的数据竞争会导致未定义的行为,您可能观察到的任何行为都不必通过交错来解释。
I tried to compile it and I constantly got the Thread ID printout as 3, but I was surprised because I thought the variable had to be global in order to be accessed by the various new threads?
不,它不必是全局的。如果以某种方式将指针或引用传递给此类变量,则线程可以访问其他线程的本地变量。
This is what I thought would happen: thread 1 is created, Fun starts to run in thread 1 with myid = 0, main thread continues running and increments i, 2nd thread is created and the myid for that would be myid=1... and so on. And so the printout would be the myID in increments i/e 1,2,3
好吧,您的程序中没有任何内容会强制这些事件按该顺序发生(或变得可观察),因此实际上没有理由期望它们会发生。完全有可能,例如,三个线程都开始了,但直到 main
中的循环完成后才有机会实际 运行,此时 i
值为 3。(或者更确切地说,i
曾经所在的内存,因为它现在超出范围并且它的生命周期已经结束 - 这是一个单独的错误,您不能阻止它发生。)
这是不会出现数据竞争的代码版本:
#include <iostream>
#include <thread>
#include <vector>
// since `id` is passed by value, each thread will work on its own copy and no
// data race is possible
void fun(int id) { std::cout << "thread id: " << id << "\n"; }
int main() {
std::vector<std::thread> threads;
for (auto id = 0; id < 3; ++id) {
threads.emplace_back(fun, id);
}
for (auto& thread : threads) {
thread.join();
}
}
由于每个线程都接收到变量 id
的副本,因此不存在竞争(除了由于不同步 std::cout
而导致的混乱输出,但我认为这不是本次讨论的一部分)。
变量不需要是全局变量就可以在多线程中使用。事实上,全局变量常常使编写多线程代码变得更加困难,甚至几乎不可能,因为不能保证每次读取和写入都会适当同步。