Я только недавно узнал о переменных условия 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 от получения мьютекса - почему это не так?
Благодарен за помощь и объяснения от сообщества.
Edit:
Я начинаю формировать намек на идею ... Я помню кое-что о pthread_cond_wait()
разблокировке его мьютекса, пока он ждет ... так что мне интересно, "отменяет" ли он мьютекс удержания ScopeLock ... ? У меня нет правильной / полностью сформированной идеи, поэтому я все еще могу использовать исчерпывающий ответ от знающих пользователей.