← Назад ко всем вопросам

Какие знаешь виды мьютексов из стандартной библиотеки

1️⃣ Как кратко ответить

В стандартной библиотеке C++ есть несколько видов мьютексов: std::mutex, std::timed_mutex, std::recursive_mutex, std::recursive_timed_mutex, std::shared_mutex и std::shared_timed_mutex. Каждый из них предоставляет различные возможности для управления доступом к общим ресурсам в многопоточных приложениях.

2️⃣ Подробное объяснение темы

Мьютексы (mutexes) — это механизмы синхронизации, которые используются для предотвращения одновременного доступа к общим ресурсам в многопоточных приложениях. Они помогают избежать состояния гонки, когда несколько потоков пытаются изменить данные одновременно, что может привести к непредсказуемым результатам.

В стандартной библиотеке C++ есть несколько видов мьютексов, каждый из которых имеет свои особенности и предназначение:

  1. std::mutex:

    • Это базовый мьютекс, который предоставляет простейший механизм блокировки. Он позволяет одному потоку захватить мьютекс, в то время как другие потоки будут заблокированы до тех пор, пока мьютекс не будет освобожден.
    • Пример использования:
      #include <iostream>
      #include <thread>
      #include <mutex>
      ​
      std::mutex mtx;
      ​
      void print_message(const std::string& message) {
          mtx.lock(); // Захватываем мьютекс
          std::cout << message << std::endl;
          mtx.unlock(); // Освобождаем мьютекс
      }
      ​
      int main() {
          std::thread t1(print_message, "Hello from thread 1");
          std::thread t2(print_message, "Hello from thread 2");
      ​
          t1.join();
          t2.join();
      ​
          return 0;
      }
      
      В этом примере std::mutex используется для синхронизации доступа к std::cout.
  2. std::timed_mutex:

    • Это расширение std::mutex, которое позволяет попытаться захватить мьютекс в течение определенного времени. Если мьютекс не может быть захвачен в течение этого времени, поток может продолжить выполнение.
    • Пример использования:
      #include <iostream>
      #include <thread>
      #include <mutex>
      #include <chrono>
      ​
      std::timed_mutex tmtx;
      ​
      void try_lock_for_example() {
          if (tmtx.try_lock_for(std::chrono::milliseconds(100))) {
              std::cout << "Lock acquired" << std::endl;
              tmtx.unlock();
          } else {
              std::cout << "Failed to acquire lock" << std::endl;
          }
      }
      ​
      int main() {
          std::thread t1(try_lock_for_example);
          std::thread t2(try_lock_for_example);
      ​
          t1.join();
          t2.join();
      ​
          return 0;
      }
      
      Здесь try_lock_for пытается захватить мьютекс в течение 100 миллисекунд.
  3. std::recursive_mutex:

    • Этот мьютекс позволяет одному и тому же потоку захватывать мьютекс несколько раз без блокировки. Это полезно в случаях, когда функция, захватывающая мьютекс, может быть вызвана рекурсивно.
    • Пример использования:
      #include <iostream>
      #include <thread>
      #include <mutex>
      ​
      std::recursive_mutex rmtx;
      ​
      void recursive_function(int count) {
          if (count <= 0) return;
      ​
          rmtx.lock();
          std::cout << "Recursion level: " << count << std::endl;
          recursive_function(count - 1);
          rmtx.unlock();
      }
      ​
      int main() {
          std::thread t1(recursive_function, 3);
          t1.join();
      ​
          return 0;
      }
      
      В этом примере std::recursive_mutex позволяет функции recursive_function захватывать мьютекс несколько раз.
  4. std::recursive_timed_mutex:

    • Это комбинация std::recursive_mutex и std::timed_mutex, позволяющая рекурсивные захваты с возможностью установки тайм-аута.
    • Пример использования аналогичен std::timed_mutex, но с возможностью рекурсивного захвата.
  5. std::shared_mutex:

    • Этот мьютекс позволяет нескольким потокам одновременно читать данные, но только одному потоку записывать. Это полезно для оптимизации производительности, когда чтение данных происходит чаще, чем запись.
    • Пример использования:
      #include <iostream>
      #include <thread>
      #include <shared_mutex>
      ​
      std::shared_mutex smtx;
      int shared_data = 0;
      ​
      void read_data() {
          smtx.lock_shared();
          std::cout << "Read data: " << shared_data << std::endl;
          smtx.unlock_shared();
      }
      ​
      void write_data(int value) {
          smtx.lock();
          shared_data = value;
          std::cout << "Wrote data: " << shared_data << std::endl;
          smtx.unlock();
      }
      ​
      int main() {
          std::thread t1(read_data);
          std::thread t2(write_data, 42);
      ​
          t1.join();
          t2.join();
      ​
          return 0;
      }
      
      В этом примере std::shared_mutex используется для разделения доступа к shared_data.
  6. std::shared_timed_mutex:

    • Это расширение std::shared_mutex, которое добавляет возможность установки тайм-аута для захвата мьютекса.
    • Пример использования аналогичен std::shared_mutex, но с возможностью использования методов try_lock_for и try_lock_until.

Каждый из этих мьютексов предоставляет различные возможности для управления доступом к общим ресурсам, и выбор конкретного типа мьютекса зависит от требований вашего приложения.

Тема: Многопоточность / Синхронизация
Стадия: Tech

🔒 Подпишись на бусти автора и стань Алигатором, чтобы получить полный доступ к функционалу сайта и отслеживать свой прогресс!

Твои заметки