2016-07-18 3 views
3

Мне было интересно, что произойдет, когда вы переместите unique_lock, который содержит recursive_mutex.Перемещение уникального_блока <recursive_mutex> в другой поток

В частности, я смотрел на этот код:

recursive_mutex g_mutex; 

#define TRACE(msg) trace(__FUNCTION__, msg) 

void trace(const char* function, const char* message) 
{ 
    cout << std::this_thread::get_id() << "\t" << function << "\t" << message << endl; 
} 

future<void> foo() 
{ 
    unique_lock<recursive_mutex> lock(g_mutex); 
    TRACE("Owns lock"); 
    auto f = std::async(launch::async, [lock = move(lock)]{ 
     TRACE("Entry"); 
     TRACE(lock.owns_lock()? "Owns lock!" : "Doesn't own lock!"); // Prints Owns lock! 
     this_thread::sleep_for(chrono::seconds(3)); 
    }); 
    TRACE(lock.owns_lock()? "Owns lock!" : "Doesn't own lock!"); // Prints Doesn't own lock! 
    return f; 
} 


int main() 
{ 
    unique_lock<recursive_mutex> lock(g_mutex); 
    TRACE("Owns lock"); 
    auto f = foo();  
    TRACE(lock.owns_lock()? "Owns lock!" : "Doesn't own lock!");  // Prints Owns lock! 
    f.wait(); 
    TRACE(lock.owns_lock()? "Owns lock!" : "Doesn't own lock!");  // Prints Owns lock! 
} 

Выход этого образца кода удивил меня много. Как unique_lock в main() знает, что поток выпустил мьютекс? Это реально?

+3

Непонятно, что вы находите удивительным. В 'unique_lock' имеется простой логический элемент' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' ' 'owns_lock()' не касается базового мьютекса. Сказав это, ваша программа проявляет неопределенное поведение: когда 'unique_lock' уничтожается в рабочем потоке, он вызывает' g_mutex.unlock() ', но рабочий поток не имеет блокировки на' g_mutex' (который является pre -requisite для 'unlock()'). –

+0

@IgorTandetnik Спасибо. Таким образом, невозможно переместить право собственности на 'recursive_mutex' между потоками? Что делать, если мьютекс не был рекурсивным? Будет ли перемещение 'unique_lock' действительно перемещать право собственности на поток владельца? –

+3

Перемещение 'unique_lock' между потоками абсолютно не подходит для вас. Поймите, что 'unique_lock' - не что иное, как указатель' mutex * 'и флаг 'bool owns' - черной магии нет. Конструктор перемещения просто перемещается по этому указателю и является логическим. Вызов 'my_mutex.unlock()' в потоке, отличном от того, который называется 'my_mutex.lock()', демонстрирует неопределенное поведение, независимо от того, сделано оно явно или косвенно, обманом 'unique_lock'. Это относится ко всем вкусам мьютексов. –

ответ

3

Вы, кажется, приписываете магические свойства unique_lock. Это не имеет, это очень простой класс. Он имеет два элемента данных: Mutex* pm и bool owns (имена участников показаны только для экспозиции). lock() - это просто pm->lock(); owns = true;, и unlock делает pm->unlock(); owns = false;. Деструктор if (owns) unlock();. Переместите конструктор копий по двум элементам и установите их в оригинале на nullptr и false, соответственно. owns_lock() возвращает значение owns участник.

Вся магия синхронизации нитей находится в самом мьютексе и его методы lock() и unlock(). unique_lock - всего лишь тонкая обертка вокруг него.

Теперь поток, вызывающий mutex.unlock() сусла, как необходимое условие, удерживая мьютекс (смысл, что нить ранее назывался lock() на нем), либо программа имеет неопределенное поведение. Это верно, если вы вызываете unlock явно, или обманите некоторого помощника, например, unique_lock, чтобы вызвать его для вас.

В свете всего этого перемещение экземпляра unique_lock в другой поток является просто рецептом для запуска неопределенного поведения вскоре после этого; нет роста.

 Смежные вопросы

  • Нет связанных вопросов^_^