繁体   English   中英

互斥锁会锁定内存的哪一部分? (p线程)

[英]What part of memory does a mutex lock? (pthreads)

我在pthreads互斥锁上阅读的所有文档仅指出,互斥锁可防止多个线程访问共享内存,但是您如何在程序中指定确切的含义呢? 是程序中的所有全局变量,还是在锁定和解锁功能之间访问的变量,还是...? 我在pthreads上发现的所有内容(包括示例)都非常令人困惑。

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

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

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

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

还可以保证将就获取或释放互斥锁对任何内存访问(读或写)进行排序。 换句话说,在保持互斥锁的同时进行的任何读取都将反映在获取互斥锁之前所做的任何更改(包括在其他线程中进行的更改),并且在保持互斥锁的同时进行的任何修改都可能对其他所有对象可见互斥锁释放时最晚线程化。 (当然,其他线程必须确保其内存读取可以使用最新的值,以使其正常工作。)

有关更多信息,您确实应该阅读David Butenhof撰写的《 使用POSIX线程编程 》。 它是参考,并对其进行了详细说明。

严格来说,互斥锁只能锁定/解锁自身。 它保护哪些共享资源完全取决于您的使用方式。 您可以使用互斥锁(或更普遍地说,是任何同步原语)来建立协议,以安全地访问共享资源(例如数据)。

例如,您有一个数组double d[10] ,该数组将由不同的线程访问。 为了保护它免受并发修改的影响,您可以创建一个互斥锁(例如, mutex_for_d ,然后对代码进行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;
};

参见以下代码:

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是密切相关的,但是这种关系仅存在于处理它们的代码中。

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

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

Mutex锁定一段代码。 例如:

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

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

互斥锁和线程独立

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

您可以说它不会锁定数据内存,因为当互斥锁被其他人锁定时,可以通过不使用互斥锁来自由地修改内存:

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM