想象一下以下场景:
#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;
}
是否有任何理由应该与向量元素一起传递互斥锁?
Pd积。从2015年5月8日起
在发帖时我没有详细说明这个问题,因为我不想影响答案。你昨天回答的问题几乎是我的理解。但是,有人向我建议,直觉行为在线程场景中可能无法像人们希望的那样可用。特别地,在这种情况下,已经建议,例如,假设在主线程上发生的写入v [0] = 1是在没有互斥的情况下将在不能保证在DoSomething中打印时反映的事物。作为一种可能的解释,我被提出值可能会进入线程可访问的内存,但是在将其写入跨线程内存之前,它可能会被另一个线程的状态换出,并且再次证明它是唯一的保证方式期望的传播将使用互斥锁。你对这个论点有什么看法?
答案 0 :(得分:4)
互斥锁用于同步多个线程之间的数据访问。如果两个或多个线程访问相同的数据,并且其中至少有一个是编写器,则需要进行同步。
在您的情况下,DoSomething
会写入您传递给它的元素。但是,在当前示例中,执行写入的线程是此时访问元素的 only 线程,因为主线程在以下连接中立即被阻止。
但是,如果主线程同时访问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
的初始写入,一切都很好,您将不会遇到任何有害的重新排序。
答案 1 :(得分:3)
没有。事实上,线程创建+立即连接使这成为一个有效(非常昂贵)的顺序函数调用。