Мне было интересно, что происходит, когда вы двигаете unique_lock
который держит 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 () знаете, что поток выпустил мьютекс? Это реально?
Вы, кажется, приписываете некоторые магические свойства 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
передача в другой поток — просто рецепт для запуска неопределенного поведения вскоре после этого; нет никаких преимуществ
Других решений пока нет …