【问题标题】:Boost, mutex concept升压,互斥锁概念
【发布时间】:2012-03-08 08:04:33
【问题描述】:
我是多线程编程的新手,对 Mutex 的工作原理感到困惑。在 Boost::Thread 手册中,它指出:
互斥锁保证只有一个线程可以锁定给定的互斥锁。如果 代码部分 被互斥锁和解锁包围,则可以保证每次只有一个线程执行该代码部分。当该线程解锁互斥锁时,其他线程可以进入该代码区域:
我的理解是,Mutex是用来保护一段代码不被多个线程同时执行的,NOT保护一个变量的内存地址。我很难理解这个概念,如果我有 2 个不同的函数试图写入同一个内存地址会发生什么。
Boost 库中是否有这样的东西:
- 锁定一个变量的内存地址,例如double x, lock(x);所以
具有不同功能的其他线程无法写入 x。
- 用 x 做某事,例如 x = x + rand();
- 解锁 (x)
谢谢。
【问题讨论】:
标签:
c++
multithreading
boost
boost-thread
【解决方案1】:
为了保护两个不同函数中的多个线程共享的内存地址,两个函数都必须使用 same 互斥体...否则您将遇到两个函数中的线程可以不加选择地访问相同的“受保护”内存区域。
所以boost::mutex 适用于您描述的场景,但您只需要确保对于您要保护的给定资源,该资源的所有路径都锁定了完全相同的boost::mutex 对象实例。
【解决方案2】:
不,boost(或其他地方)没有任何东西会像那样锁定内存。
您必须保护访问您想要保护的内存的代码。
如果我有 2 个不同的函数试图写入同一个函数会发生什么
内存地址。
假设您的意思是 2 个函数在不同的线程中执行,这两个函数都应该锁定 same 互斥体,因此在给定时间只有一个线程可以写入变量。
访问(读取或写入)相同变量的任何其他代码也必须锁定相同的互斥体,否则将导致不确定的行为。
【解决方案3】:
互斥锁本身只确保只有一个执行线程可以在任何给定时间锁定互斥锁。您可以确保仅在互斥锁被锁定时修改关联变量。
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 对象视为一个正常变量。
但是,当您这样做时,每次您为其分配值时,互斥锁都会自动锁定,然后立即解锁。当然,这几乎是最简单的情况——在许多情况下,您需要执行一些操作,例如锁定互斥锁,同时修改两个(或更多)变量,然后解锁。然而,不管复杂性如何,您的想法仍然是您将所有进行修改的代码集中在一个地方,因此您不必担心将互斥锁锁定在其余代码中。如果您确实有两个或多个变量像这样在一起,您通常必须锁定互斥锁以读取,而不仅仅是写入 - 否则您很容易得到一个不正确的值,其中一个变量已被修改但另一个变量没有t.
【解决方案4】:
您对互斥体的理解是正确的。它们保护锁定和解锁之间的代码段。
根据两个线程写入同一内存位置时发生的情况,它们被序列化。一个线程写入它的值,另一个线程写入它。这样做的问题是你不知道哪个线程会先写(或最后写),所以代码不是确定性的。
最后,为了保护变量本身,您可以在原子变量中找到一个近乎概念。原子变量是受编译器或硬件保护的变量,可以原子地修改。也就是说,您评论的三个阶段(读取、修改、写入)以原子方式发生。看看 Boost atomic_count。
【解决方案5】:
我认为您缺少的细节是“代码部分”是代码的任意部分。它可以是两个函数、半个函数、单行或其他。
因此,当您的 2 个不同函数在访问共享数据时拥有相同互斥锁的部分,是“由互斥锁锁定和解锁包围的代码部分”,因此“保证只有一次一个线程执行那段代码”。
另外,这是解释互斥体的一个属性。它并没有声称这是他们拥有的唯一财产。
【解决方案6】:
可以使用Boost.Atomic 对某些类型执行非阻塞原子操作。这些操作是非阻塞的,通常比互斥锁快得多。例如,要以原子方式添加某些内容,您可以这样做:
boost::atomic<int> n = 10;
n.fetch_add(5, boost:memory_order_acq_rel);
此代码自动将 5 添加到 n。