Может ли использование блокировки мьютекса, принятого lock_guard, привести к UB?

Может ли следующий фрагмент вызвать неповрежденное поведение из-за использования блокировки мьютекса, уже принятого lock_guard? и будет ли безопасно, если я используюunique_lock вместо того lock_guardв том же фрагменте? Я знаю что естьstd::unique_lock<T>::lock/unlock()

#include <iostream>
#include <thread>
#include <mutex>
#include <chrono>

std::mutex m1;
void func(int count ){

    std::lock_guard lG{m1};
    std::cout << std::this_thread::get_id() << std::endl;
    if(count == 1) {
        m1.unlock();
        std::this_thread::sleep_for(std::chrono::duration<size_t, std::ratio<1, 1>>{6});
        m1.lock();
    }
    std::cout << std::this_thread::get_id() << std::endl;
}
int main()
{
    std::thread t1 {func, 1};
    std::thread t2 {func, 9};
    t1.join();
    t2.join();
}

1 ответ

Решение

Этот конкретный код, вероятно, безопасен, но я бы не считал его хорошим стилем. Проблема в том, что если что-то вызывает исключение междуm1.unlock() а также m1.lock(), то lock_guarddestructor собирается разблокировать разблокированный мьютекс во второй раз, вызывая UB. Таким образом, даже если все, что находится между этими операторами, гарантированно не бросит, читатель кода должен слишком внимательно изучить этот код, чтобы убедиться, что UB отсутствует.

Было бы намного лучше использовать unique_lock и сделайте танец разблокировки / блокировки наunique_lock вместо того, чтобы прямо на mutex для обеспечения правильной разблокировки в исключительном случае.

Другие вопросы по тегам