我最近才刚刚了解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的预期互斥锁。 ..?不过,我没有一个适当/完整的想法,因此我仍然可以使用博学的用户的全面答案。
答案 0 :(得分:3)
目的在于,由于线程1获取互斥量并阻塞 线程2尝试获取时也会永远阻塞 互斥锁。
这些函数自动释放互斥锁,并导致调用线程在条件变量cond上阻塞;
因此,线程1释放了互斥锁,线程2愉快地使用了该互斥锁。
这没关系,因为pthread_cond_wait
在返回之前重新获取了互斥量,这使您的使用非常完美:
成功返回后,互斥锁应已锁定,并且必须 调用线程拥有。
此问题可能对理解为什么如此起作用感兴趣:Why do pthreads’ condition variable functions require a mutex?