2

我是并发新手,我对std::mutex. 假设我有一个 int a; 书籍告诉我要声明互斥体;获得对 a 的独占访问权。现在我的问题是互斥对象如何识别它必须保护的关键资源?我的意思是哪个变量?假设我有两个变量 int a,b; 现在我声明互斥量 abmut;现在abmut会保护什么???a 和 b 或只有 a 或 b ???

4

6 回答 6

7

你的怀疑是有道理的:它没有。这是您作为程序员的工作,以确保您只有a在拥有互斥锁的情况下才能访问。如果其他人获得了互斥锁,请不要访问,a否则您将遇到与没有互斥锁时相同的问题。这适用于所有线程同步结构。可以使用它们来保护资源。他们不是自己做的。

于 2013-07-11T05:50:03.190 回答
3

互斥锁更像是一个标志而不是锁。当有人在公共洗手间看到“有人”的标志时,他会等到用户出来并翻转标志。但是你必须教他看到标志时等待。标志本身并不会阻止他闯入。当然,“等待”的顺序已经由 mutex.lock() 设置,因此您可以方便地使用它。

于 2013-07-11T06:27:51.213 回答
2

Astd::mutex根本不保护任何数据。互斥锁的工作方式如下:

  • 当您尝试锁定互斥锁时,您会查看互斥锁是否尚未锁定,否则您等待直到它被解锁。
  • 当您使用完互斥锁后,您将其解锁,否则等待的线程将永远这样做。

那如何保护东西?考虑以下:

#include <iostream>
#include <future>
#include <vector>

struct example {
    static int shared_variable;
    static void incr_shared() 
    {
        for(int i = 0; i < 100000; i++)
        {
            shared_variable++; 
        }
    }
};
int example::shared_variable = 0;

int main() {
    std::vector<std::future<void> > handles;
    handles.reserve(10000);
    for(int i = 0; i < 10000; i++) {
        handles.push_back(std::async(std::launch::async, example::incr_shared));
    }
    for(auto& handle: handles) handle.wait();
    std::cout << example::shared_variable << std::endl;
}

您可能希望它打印 1000000000,但您并不能真正保证这一点。我们应该包括一个互斥体,如下所示:

struct example {
    static int shared_variable;
    static std::mutex guard;
    static void incr_shared() 
    {
        std::lock_guard<std::mutex>{ guard };
        for(int i = 0; i < 100000; i++)
        {
            shared_variable++; 
        }
    }
};

那么这到底是做什么的呢?首先,在创建和销毁时std::lock_guard使用RAII调用,最后一个发生在它离开作用域时(这里是函数退出时)。所以在这种情况下,只有一个线程可以执行 for 循环,因为一旦一个线程通过它,它就会持有锁,而我们之前看到没有其他线程可以持有它。因此这个循环现在是安全的。请注意,我们也可以将循环放在内部,但这可能会使您的程序变慢(锁定和解锁相对昂贵)。mutex.lock()mutex.unlocklock_guardlock_guard

所以总而言之,amutex保护代码块,在我们的示例中是 for 循环,而不是变量本身。如果您想要变量保护,请考虑查看std::atomic. 例如,以下示例再次不安全,因为decr_shared可以从任何线程同时调用。

struct example {
    static int shared_variable;
    static std::mutex guard;

    static void decr_shared() { shared_variable--; }
    static void incr_shared() 
    {
        std::lock_guard<std::mutex>{ guard };
        for(int i = 0; i < 100000; i++)
        {
            shared_variable++; 
        }
    }
};

然而,这又是安全的,因为现在变量本身在任何使用它的代码中都受到保护。

struct example {
    static std::atomic_int shared_variable;

    static void decr_shared() { shared_variable--; }
    static void incr_shared() 
    {
        for(int i = 0; i < 100000; i++)
        {
            shared_variable++; 
        }
    }
};
std::atomic_int example::shared_variable{0};
于 2013-07-11T06:36:36.947 回答
0

Imagine you sit at a table with your friends and a delicious cake (the resources you want to guard, e.g. some integer a) in the middle. In addition you have a single tennis ball (this is our mutex). Now, only a single person can have the ball (lock the mutex using a lock_guard or similar mechanisms), but everyone can eat the cake (access the integer a).

As a group you can decide to set up a rule that only whoever has the ball may eat from the cake (only the person who has locked the mutex may access a). This person may relinquish the ball by putting it on the table (unlock the mutex), so another person can grab it (lock the mutex). This way you ensure that no one stabs another person with the fork, while frantically eating the cake.

Setting up and upholding a rule like described in the last paragraph is your job as a programmer. There is no inherent connection between a mutex and some resource (e.g. some integer a).

于 2013-07-11T06:22:33.340 回答
0

互斥锁用于同步对资源的访问。假设您有一个数据说 int,您将在其中使用 getter 和 setter 进行读写操作。因此,getter 和 setter 都将使用相同的互斥锁来同步读/写操作。

这两个函数都会在开始时锁定互斥锁并在它返回之前将其解锁。您可以使用将在其析构函数上自动解锁的 scoped_lock。

void setter(value_type v){
  boost::mutex::scoped_lock lock(mutex);
  value = v;
}    
value_type getter() const{
  boost::mutex::scoped_lock lock(mutex);
  return value;
}
于 2013-07-11T06:11:34.510 回答
0

互斥体本身并不保护任何特定变量……相反,程序员需要意识到他们有一组 1 个或多个变量,多个线程可能会尝试使用,然后使用互斥体,这样只有其中一个线程可以在任何时间点运行此类变量使用/更改代码。

请特别注意,只有在其他线程的代码在变量访问期间锁定相同的互斥锁时,您才会受到其他线程的代码访问/修改这些变量的保护。只有一个线程使用的互斥锁不能保护任何东西。

于 2013-07-11T06:16:50.057 回答