Boost,互斥概念

Boost, mutex concept

本文关键字:Boost      更新时间:2023-10-16

我是多线程编程的新手,对Mutex的工作方式感到困惑。在Boost::Thread手册中,它指出:

互斥保证只有一个线程可以锁定给定的互斥。如果代码段被互斥锁锁定和解锁所包围,则可以保证一次只有一个线程执行该代码段。当该线程解锁互斥锁时,其他线程可以进入该代码区域:

我的理解是Mutex用于保护一段代码不被多个线程同时执行,NOT保护变量的内存地址。我很难理解这个概念,如果我有两个不同的函数试图写入同一个内存地址,会发生什么。

在Boost库中有这样的东西吗:

  1. 锁定变量的存储器地址,例如,double x,lock(x);所以具有不同函数的其他线程无法写入x
  2. 用x做一些事情,例如,x=x+rand()
  3. 解锁(x)

谢谢。

互斥体本身只确保在任何给定时间只有一个执行线程可以锁定互斥体。这取决于您确保只有在互斥锁被锁定时才会对关联变量进行修改。

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对象视为一个普通变量。

然而,当您这样做时,每次为互斥对象赋值时,它都会自动锁定,然后立即解锁。当然,这几乎是最简单的情况——在许多情况下,您需要做一些事情,比如锁定互斥锁,同时修改两个(或多个)变量,然后解锁。然而,不管复杂程度如何,我们的想法仍然是将所有进行修改的代码集中在一个地方,这样就不必担心在代码的其余部分锁定互斥对象。如果你有两个或多个这样的变量在一起,你通常必须锁定互斥对象进行读取,而不仅仅是写入——否则你很容易得到一个不正确的值,其中一个变量已经修改,但另一个没有修改。

不,boost(或其他地方)中没有任何东西会像那样锁定内存。您必须保护访问您想要保护的内存的代码。

如果我有两个不同的函数试图写入同一个函数,会发生什么内存地址。

假设有两个函数在不同的线程中执行,两个函数都应该锁定相同的互斥锁,因此在给定的时间只有一个线程可以写入变量。

任何其他访问(读取或写入)同一变量的代码也必须锁定同一互斥对象,否则将导致不确定性行为。

使用Boost.atomic可以对某些类型执行非阻塞原子操作。这些操作是非阻塞的,通常比互斥锁快得多。例如,要添加原子性的东西,您可以执行以下操作:

boost::atomic<int> n = 10;
n.fetch_add(5, boost:memory_order_acq_rel);

此代码将5原子性地添加到n

为了保护两个不同函数中多个线程共享的内存地址,两个函数都必须使用相同的互斥锁。。。否则,您将遇到这样一种情况:任一函数中的线程都可以不加区分地访问同一个"受保护"内存区域。

因此,boost::mutex适用于您描述的场景,但您只需要确保对于您保护的给定资源,该资源的所有路径都锁定了boost::mutex对象的完全相同的实例。

我认为您缺少的细节是"代码段"是代码的任意部分。它可以是两个函数,半个函数,一行,或者其他什么。

因此,当您的两个不同函数访问共享数据时,持有相同互斥对象的部分"一个被互斥对象锁定和解锁包围的代码段",因此"保证一次只有一个线程执行该代码段"。

同时,这也解释了互斥的一个属性。它并没有声称这是他们唯一的财产。

您对互斥的理解是正确的。它们保护锁定和解锁之间的代码段。

根据两个线程写入同一内存位置时发生的情况,它们被序列化。一个线程写入它的值,另一个线程向它写入。问题是你不知道哪个线程将首先(或最后)写入,所以代码是不确定的。

最后,为了保护变量本身,可以在原子变量中找到一个近似的概念。原子变量是受编译器或硬件保护的变量,可以进行原子修改。也就是说,您注释的三个阶段(读取、修改、写入)是原子发生的。看看Boost atomic_count