为什么这个非递归互斥量可以获取两次呢?
Why can this non-recursive mutex be acquired twice?
我最近才了解 pthread 条件变量,这似乎是这个问题的基础。
我正在观察似乎是一个线程的东西 "breaking through" 并获取另一个线程拥有的互斥体!
这颠覆了我对互斥锁所有权的基本理解,我不知该如何解释:
在下面的代码中,我有 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;
}
Compilation/output:
>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 不会超出 func2()
.
中的 "locking mutex..."
语句
所以我推测此应用程序的 "unexpected" 行为是因为 pthread_cond_wait()
,但我想我根本不明白为什么:为什么线程 2 可以超越互斥量获取?我的预期是线程 1 已获取互斥锁,并等待一个从未发出信号的条件变量,这会阻止线程 2 获取互斥锁 - 为什么不是这样?
感谢社区的帮助和解释。
编辑:
我开始形成一个想法......我记得一些关于 pthread_cond_wait()
在它等待时解锁它的互斥体......所以我想知道它是否是 "undoing" ScopeLock 的目的互斥保持...?不过,我没有 proper/fully-formed 想法,所以我仍然可以使用知识渊博的用户提供的全面答案。
The intent is that, since thread 1 acquires the mutex and blocks
forever, thread 2 will also block forever when it attempts to acquire
the mutex.
These functions atomically release mutex and cause the calling thread to block on the condition variable cond;
因此,线程 1 释放了线程 2 愉快使用的互斥锁。
没关系,因为 pthread_cond_wait
在返回之前重新获取互斥量,这使您的使用非常好:
Upon successful return, the mutex shall have been locked and shall be
owned by the calling thread.
这个问题可能有助于理解为什么它会这样工作:Why do pthreads’ condition variable functions require a mutex?
我最近才了解 pthread 条件变量,这似乎是这个问题的基础。
我正在观察似乎是一个线程的东西 "breaking through" 并获取另一个线程拥有的互斥体!
这颠覆了我对互斥锁所有权的基本理解,我不知该如何解释:
在下面的代码中,我有 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;
}
Compilation/output:
>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 不会超出 func2()
.
"locking mutex..."
语句
所以我推测此应用程序的 "unexpected" 行为是因为 pthread_cond_wait()
,但我想我根本不明白为什么:为什么线程 2 可以超越互斥量获取?我的预期是线程 1 已获取互斥锁,并等待一个从未发出信号的条件变量,这会阻止线程 2 获取互斥锁 - 为什么不是这样?
感谢社区的帮助和解释。
编辑:
我开始形成一个想法......我记得一些关于 pthread_cond_wait()
在它等待时解锁它的互斥体......所以我想知道它是否是 "undoing" ScopeLock 的目的互斥保持...?不过,我没有 proper/fully-formed 想法,所以我仍然可以使用知识渊博的用户提供的全面答案。
The intent is that, since thread 1 acquires the mutex and blocks forever, thread 2 will also block forever when it attempts to acquire the mutex.
These functions atomically release mutex and cause the calling thread to block on the condition variable cond;
因此,线程 1 释放了线程 2 愉快使用的互斥锁。
没关系,因为 pthread_cond_wait
在返回之前重新获取互斥量,这使您的使用非常好:
Upon successful return, the mutex shall have been locked and shall be owned by the calling thread.
这个问题可能有助于理解为什么它会这样工作:Why do pthreads’ condition variable functions require a mutex?