想象一下以下场景:
#include <chrono>
#include <iostream>
#include <thread>
#include <vector>
void DoSomething(int* i)
{
std::cout << *i << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(1));
*i = 2;
std::cout << *i << std::endl;
}
int main()
{
std::vector<int> v = {0, 0, 0};
v[0] = 1;
std::this_thread::sleep_for(std::chrono::seconds(1));
std::thread t(&DoSomething, &v[0]);
t.join();
std::cout << v[0] << std::endl;
}
是否有任何理由认为应该将互斥量与 vector 元素一起传递?
PD从 08/May/2015
我在发帖时没有详细说明这个问题,因为我不想影响答案。直到昨天,你的回答几乎都是我的理解。然而,有人向我建议,在线程场景中,直觉行为可能不像人们希望的那样有用。特别是,在这种情况下,有人建议,例如,假设在主线程上发生的写入 v[0] = 1 是在没有互斥体的情况下在 DoSomething 中打印时会反射(reflect)出来的假设是不能保证的。作为一种可能的解释,有人向我提供了该值可能进入线程可访问内存,但它可能在写入跨线程内存之前被不同线程的状态换出,并且再次保证唯一的方法所需的传播将使用互斥锁。你对这个论点有什么看法?
最佳答案
互斥量用于同步多个线程之间的数据访问。如果两个或多个线程访问相同的数据并且其中至少一个是写入器,则它们需要同步。
在您的情况下,DoSomething
会写入您传递给它的元素。但是,在您当前的示例中,执行写入的线程是当时访问元素的唯一线程,因为主线程在接下来的连接中立即被阻塞。
但是,如果主线程将同时访问 v
,则您需要保护该访问:
// BROKEN CODE BELOW
int main()
{
std::vector<int> v = {0, 0, 0};
std::thread t(&DoSomething, &v[0]);
// swapped the join and the cout; now cout accesses v[0]
// while DoSomething writes to it, which is bad;
std::cout << v[0] << std::endl;
t.join();
}
至于您的编辑:您基本上担心 v[0]
的初始分配可能会被重新排序,因此它对 DoSomething
线程不可见。原则上,写入可以通过并发线程观察到的值与人们直观预期的值不同的方式重新排序。防止这种情况的唯一方法是插入 memory barriers明确强制执行某种顺序。在 C++ 中,这通常通过 std::mutex
或 atomics 等线程同步原语发生。
幸运的是,the creation of a thread acts as such a memory barrier .因此,在您的示例中,对 v
的初始写入发生在创建线程之前,一切都很好,您不会遇到任何有害的重新排序。
关于c++ - C++ 线程中未触及的共享资源,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30100167/