首页 > 解决方案 > 为什么这个非递归互斥锁可以被获取两次?

问题描述

我最近才学习 pthread 条件变量,这似乎是这个问题的基础。

我正在观察似乎是一个线程“突破”并获得另一个线程拥有的互斥锁!

这打破了我对互斥锁所有权的理解的基础,我不知道如何解释这一点:

在下面的代码中,我有class ScopeLock一个相当常见的 C++ 对互斥锁的包装器,它在其 ctor 中获取互斥锁并在其 dtor 中释放它。

从 开始main(),我产生了两个线程,每个线程都试图获取一个公共互斥体。因为在两个线程创建之间存在健康睡眠,所以预计第一个生成的线程将获取互斥锁。

在线程 1 中,我做了一个pthread_cond_wait()并且从不向条件变量发出信号,目的是永远阻塞。

意图是,由于线程 1 获取互斥锁并永远阻塞,线程 2 在尝试获取互斥锁时也将永远阻塞。

代码:

// main.cpp

#include <iostream>
#include <pthread.h>
#include <unistd.h>

class ScopeLock
{
public:

  ScopeLock( pthread_mutex_t& mutex ) : mutex_( mutex )
  {
    pthread_mutex_lock( &mutex );
  }

  ~ScopeLock()
  {
    pthread_mutex_unlock( &mutex_ );
  }

private:

  pthread_mutex_t mutex_;
};

pthread_mutex_t g_mutex;
pthread_cond_t g_cond;

void* func1( void* arg )
{
  std::cout << "locking g_mutex from " << pthread_self() << std::endl;
  ScopeLock lock( g_mutex );
  std::cout << "locked g_mutex from " << pthread_self() << std::endl;

  std::cout << __FUNCTION__ << " before cond_wait()" << std::endl;
  pthread_cond_wait( &g_cond, &g_mutex );
  //sleep( 1000 );
  std::cout << __FUNCTION__ << " after cond_wait()" << std::endl;
  return NULL;
}

void* func2( void* arg )
{
  std::cout << "locking g_mutex from " << pthread_self() << std::endl;
  ScopeLock lock( g_mutex );
  std::cout << "locked g_mutex from " << pthread_self() << std::endl;

  std::cout << __FUNCTION__ << std::endl;
  return NULL;
}

int main( int argc, char* argv[] )
{
  pthread_t t1;
  pthread_t t2;

  pthread_mutex_init( &g_mutex, NULL );
  pthread_cond_init( &g_cond, NULL );

  pthread_create( &t1, NULL, func1, NULL );

  sleep ( 2 );

  pthread_create( &t2, NULL, func2, NULL );

  pthread_join( t2, NULL );
  std::cout << "joined t2" << std::endl;
  pthread_join( t1, NULL );
  std::cout << "joined t1" << std::endl;

  return 0;
}

编译/输出:

>g++ --version
g++ (GCC) 4.8.3 20140911 (Red Hat 4.8.3-7)
Copyright (C) 2013 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

>g++ -g main.cpp -lpthread && ./a.out
locking g_mutex from 139707808458496
locked g_mutex from 139707808458496
func1 before cond_wait()
locking g_mutex from 139707800065792 // <-- Here onward is output 2 sec later
locked g_mutex from 139707800065792
func2
joined t2

但是可执行文件的输出显示线程 2 前进超过了互斥量获取!谁能解释为什么会这样?

您可以看到我试图用“”检查情况sleep( 1000 ):如果我注释掉pthread_cond_wait()并取消注释sleep(),那么可执行行为与我的预期一致,即线程 2 不会"locking mutex..."超出func2().

所以我推测这个应用程序的“意外”行为是因为pthread_cond_wait(),但我想我根本不明白为什么:为什么线程 2 可以超越互斥体获取?我的期望是线程 1,已经获取了互斥锁,并等待一个从未发出信号的条件变量会阻止线程 2 获取互斥锁 - 为什么不是这样?

感谢社区的帮助和解释。

编辑:

我开始形成一个想法的暗示......我记得pthread_cond_wait()在它等待时解锁它的互斥锁......所以我想知道它是否正在“撤消” ScopeLock 的预期互斥锁......?不过,我没有一个正确/完整的想法,所以我仍然可以使用来自知识渊博的用户的全面答案。

标签: c++pthreadsmutexcondition-variable

解决方案


其意图是,由于线程 1 获取互斥锁并永远阻塞,线程 2 在尝试获取互斥锁时也将永远阻塞。

文档中

这些函数以原子方式释放互斥体并导致调用线程阻塞条件变量 cond;

因此,线程 1 释放了线程 2 愉快使用的互斥锁。

不过没关系,因为pthread_cond_wait在返回之前重新获取互斥锁,这使您的使用非常好:

成功返回后,互斥锁将被锁定并归调用线程所有。

这个问题可能有助于理解它为什么会这样工作:为什么 pthreads 的条件变量函数需要互斥锁?


推荐阅读