我是多线程编程的新手,并且对Mutex的工作原理感到困惑。在Boost :: Thread手册中,它指出:
互斥锁保证只有一个线程可以锁定给定的互斥锁。如果代码部分被互斥锁定和解锁包围,则可以保证一次只有一个线程执行该部分代码。当该线程解锁互斥锁时,其他线程可以进入该代码区域:
我的理解是,Mutex用于保护一段代码不被多个线程同时执行, NOT 保护变量的内存地址。我很难掌握这个概念,如果我有2个不同的函数试图写入相同的内存地址会发生什么。
在Boost库中是否有这样的东西:
感谢。
答案 0 :(得分:5)
互斥锁本身只能确保在任何给定时间只有一个执行线程可以锁定互斥锁。您可以确保仅在互斥锁被锁定时才会修改关联变量。
C ++确实为你提供了一种比C语言更容易实现的方法。在C语言中,正是由你来正确编写代码,确保在修改变量的任何地方,你首先锁定互斥锁(当然,当你完成时解锁它。)
在C ++中,将它全部封装到一个运算符重载的类中非常容易:
class protected_int {
int value; // this is the value we're going to share between threads
mutex m;
public:
operator int() { return value; } // we'll assume no lock needed to read
protected_int &operator=(int new_value) {
lock(m);
value = new_value;
unlock(m);
return *this;
}
};
显然我正在简化这一点(到目前为止它可能毫无用处),但希望你能得到这个想法,即大多数代码只是将protected_int
对象看作是它是一个正常的变量。
但是,当您这样做时,每次为其分配值时,互斥锁都会自动锁定,之后立即解锁。当然,这几乎是最简单的情况 - 在许多情况下,你需要做一些事情,比如锁定互斥锁,同时修改两个(或更多)变量,然后解锁。然而,无论复杂程度如何,您仍然希望将所有修改代码集中在一个地方,因此您不必担心在其余代码中锁定互斥锁。如果您有两个或多个变量,那么通常必须锁定互斥锁才能读取 - 而不仅仅是写入 - 否则您可以很容易地得到一个不正确的值,其中一个变量已被修改,但另一个变量已经被修改吨。
答案 1 :(得分:3)
不,升级(或其他地方)没有任何内容可以像这样锁定内存。 您必须保护访问您想要保护的内存的代码。
如果我有2个不同的函数试图写入相同的内容会发生什么 记忆地址。
假设你的意思是在不同的线程中执行两个函数,两个函数都应该锁定相同的互斥锁,因此只有一个线程可以在给定的时间写入变量。
访问(读取或写入)同一个变量的任何其他代码也必须锁定相同的互斥锁,否则将导致不确定的行为。
答案 2 :(得分:2)
可以使用Boost.Atomic对某些类型执行非阻塞原子操作。这些操作是非阻塞的,通常比互斥锁快得多。例如,要以原子方式添加内容,您可以执行以下操作:
boost::atomic<int> n = 10;
n.fetch_add(5, boost:memory_order_acq_rel);
此代码原子地将5添加到n
。
答案 3 :(得分:1)
为了保护两个不同函数中多个线程共享的内存地址,两个函数都必须使用相同的互斥锁...否则你将遇到任一函数中的线程可以使用的场景不分青红皂白地访问相同的“受保护”内存区域。
所以boost::mutex
适用于您描述的场景,但您必须确保对于您要保护的给定资源,该资源的所有路径都锁定{{1的完全相同的实例对象。
答案 4 :(得分:1)
我认为您缺少的细节是“代码部分”是代码的任意部分。它可以是两个函数,半个函数,一个单行或其他任何函数。
因此,当您访问共享数据时,2个不同函数中包含相同互斥锁的部分 “由互斥锁定和解锁所围绕的代码部分”因此“仅保证”一次一个线程执行该段代码“。
此外,这是解释互斥体的一个属性。并不是说这是他们唯一拥有的财产。
答案 5 :(得分:0)
您对互斥体的理解是正确的。它们保护锁定和解锁之间的代码段。
根据两个线程写入内存的相同位置时发生的情况,它们被序列化。一个线程写入其值,另一个线程写入它。这个问题是你不知道哪个线程会先写(或最后一个),所以代码不是确定的。
最后,为了保护变量本身,您可以在原子变量中找到近似概念。原子变量是受编译器或硬件保护的变量,可以原子方式进行修改。也就是说,您评论(读取,修改,写入)的三个阶段是以原子方式发生的。看看Boost atomic_count
。