7

我在 pthreads 互斥锁上阅读的所有文档都只声明互斥锁阻止多个线程访问共享内存,但是您如何在程序中指定它到底是什么?是程序中的所有全局变量,锁定和解锁函数之间访问的变量,还是......?我在 pthreads 上找到的所有内容,包括示例,都非常模糊。

4

7 回答 7

16

互斥锁阻止多个线程访问共享内存

以上是不正确的说法。就其本身而言,互斥锁不会这样做。它允许您构建代码以防止多个线程同时访问共享内存或其他资源,但它本身不会锁定任何东西。

您可以构建一个使用互斥锁的程序,以防止多个线程同时执行特定的代码片段。如果这些代码恰好正在访问共享内存区域,并且没有其他代码会尝试在不锁定互斥锁的情况下同时访问该区域,那么效果将是“锁定”该内存区域。

于 2013-10-03T08:13:07.627 回答
10

互斥锁不锁定内存,它“锁定”执行路径的一部分,并同步内存(但在锁定和解锁时)。可以保证的是,如果一个线程持有互斥锁,其他线程就无法获取它,并且任何试图获取它的线程都将被阻塞,直到它被释放。

还保证任何内存访问(读取或写入)都将根据获取或释放互斥锁进行排序;换句话说,在持有互斥锁期间所做的任何读取都将反映在获取互斥锁之前所做的任何更改(包括在不同线程中所做的更改),并且在持有互斥锁期间所做的任何修改都可能对所有其他人可见最迟在释放互斥锁时线程。(当然,其他线程必须确保它们的内存读取看到最新的值才能正常工作。)

有关更多信息,您真的应该阅读David Butenhof 的Programming with POSIX Threads。这是参考,并详细解释了它们。

于 2013-10-03T08:13:04.083 回答
3

严格来说,互斥锁只会锁定/解锁自己。它保护的共享资源完全取决于您如何使用它。您使用互斥锁(或更一般地说是任何同步原语)为自己建立一个协议,以安全地访问数据等共享资源。

例如,您有一个double d[10]可供不同线程访问的数组。为了保护它免受并发修改,您可以创建一个互斥体,比如说mutex_for_d,然后对您的代码进行编程,以便每次任何代码访问d时,它都会首先锁定mutex_for_d。这样,访问d受到互斥锁的保护。

或者,您可以决定将数组的每个元素单独同步 - 并拥有一个互斥体数组,始终锁定您访问的元素的一个。

这纯粹是您自己的协议,您必须确保遵守它 - 如果您忘记将互斥锁锁定在一个修改d的函数中,程序仍将运行,但可能会引入数据竞争。出于这个原因,您通常希望将共享数据隐藏在可以保证正确锁定的类接口后面,如下所示:

struct SharedArray
{
  double get(size_t idx) const { std::lock_guard<std::mutex> lock(mut); return d[idx]; }
  void set(size_t idx, double v) { std::lock_guard<std::mutex> lock(mut); d[idx] = v; }
private:
  double d[10];
  std::mutex mut;
};
于 2013-10-03T08:18:16.727 回答
1

请参阅此代码:

bool initialized_array = false;

int some_array[10];

void do_some_initialization() { ... };

int get_array_element(int i)
{
    if (!initialized_array) {
        do_some_initialization();
        initialized_array = true;
    }
    return some_array[i];
}

如您所见,变量initialized_arraysome_array密切相关,但这种关系只存在于处理它们的代码中。

这就是互斥锁与共享内存相关联的方式。关联的发生是因为您编写了这样做的代码。没有办法说“这个互斥体保护了那个共享对象”,程序员必须确保每次线程访问共享对象时,它也在正确的互斥体上执行同步。

于 2013-10-03T08:12:40.200 回答
1

锁定时,互斥锁会阻止任何其他线程获得对同一互斥锁的锁定。因此,如果您希望某些特定数据是线程安全的(即,内存的关键部分),您应该在读取或写入互斥锁之前获得一个锁,然后在完成时释放锁。互斥锁本身并不引用任何特定的内存部分。它只是一个您可以使用的工具。

于 2013-10-03T08:13:01.850 回答
1

互斥锁锁定一段代码。例如:

mutex.lock();
    //some code here
mutex.unlock();

如果一个线程进入上面的代码,它会锁定它,直到它完成。同时没有其他线程可以执行这段代码。

互斥量和线程独立性

于 2013-10-03T08:13:15.173 回答
1

互斥锁会锁定自己,这就是它锁定的全部内容。它不会锁定其他内存,也不会锁定代码。您可以设计代码,以便保护互斥锁以外的其他内容,但这取决于您设计代码的方式,而不是互斥锁本身的功能。

您可以告诉它不会锁定数据内存,因为您可以在互斥锁被其他人锁定时自由修改内存,只需不使用互斥锁即可:

thread1:            thread2:
    lock mtx            set i to 4  // i is not protected here
    set i to 7
    unlock mtx

说它锁定代码也不完全正确,因为您可以在单个互斥锁的控制下运行各种不同的代码部分。

而且,如果有人设法在没有首先声明互斥锁的情况下访问互斥锁块中的代码,即使其他人锁定了互斥锁,它也可以自由运行代码:

threadN:
    if thread_id == 2: goto skip_label1
    lock mtx
  :skip1_label1

    set i to 7              // not as protected as you think

    if thread_id == 2: goto skip_label2
    unlock mtx
  :skip1_label2
于 2013-10-03T08:16:24.477 回答