Biblioteca estándar de C++<mutex>
En la programación multihilo, garantizar la consistencia de los datos y la seguridad de los subprocesos es crucial.
La biblioteca estándar de C++ proporciona un rico conjunto de primitivas de sincronización para controlar el acceso a los recursos compartidos.
En la biblioteca estándar de C++,<mutex>El archivo de cabecera proporciona un conjunto de herramientas para implementar la sincronización y exclusión mutua entre subprocesos en programas multihilo.
<mutex>El archivo de cabecera fue introducido en C++11 y contiene clases y funciones para exclusión mutua (mutex). El mutex es un mecanismo de sincronización que se utiliza para evitar que varios subprocesos accedan simultáneamente a recursos compartidos.
El mutex es una primitiva de sincronización utilizada para controlar el acceso a los recursos compartidos. Cuando un subproceso necesita acceder a un recurso compartido, intenta bloquear el mutex. Si el mutex ya está bloqueado por otro subproceso, el subproceso solicitante se bloqueará hasta que se libere el mutex.
Sintaxis básica
En C++,<mutex>El archivo de cabecera proporciona las siguientes clases principales:
std::mutex: mutex básico.std::recursive_mutex: mutex recursivo, permite que el mismo subproceso lo bloquee varias veces.std::timed_mutex: mutex con función de tiempo de espera.std::recursive_timed_mutex: mutex recursivo con función de tiempo de espera.
Ejemplo
1. Usostd::mutex
A continuación se muestra un ejemplo sencillo que demuestra cómo usar en C++std::mutexpara sincronizar el acceso a los recursos compartidos.
Ejemplo
#include <thread>
#include <mutex>
std::mutex mtx; // mutex global
int shared_resource = 0;
void increment() {
for (int i = 0; i < 10000; ++i) {
mtx.lock(); // bloquear el mutex
++shared_resource;
mtx.unlock(); // desbloquear el mutex
}
}
int main() {
std::thread t1(increment);
std::thread t2(increment);
t1.join();
t2.join();
std::cout << "Final value of shared_resource: " << shared_resource << std::endl;
return 0;
}
Salida:
Final value of shared_resource: 20000
2. Usostd::recursive_mutex
El mutex recursivo permite que el mismo subproceso bloquee el mismo mutex varias veces. A continuación se muestra un uso destd::recursive_mutexejemplo.
Ejemplo
#include <thread>
#include <mutex>
std::recursive_mutex rmtx; // crear un objeto mutex recursivo
int shared_resource = 0; // recurso compartido
// función recursiva
void recursive_increment(int count) {
if (count <= 0) return;
std::lock_guard<std::recursive_mutex> lock(rmtx); // bloquear para garantizar la seguridad del subproceso
++shared_resource;
std::cout << "Incremented shared_resource to " << shared_resource << " (count = " << count << ")" << std::endl;
// llamada recursiva
recursive_increment(count - 1);
}
int main() {
std::thread t1(recursive_increment, 3); // el subproceso t1 ejecuta recursive_increment(3)
std::thread t2(recursive_increment, 3); // el subproceso t2 ejecuta recursive_increment(3)
t1.join(); // esperar a que el subproceso t1 termine
t2.join(); // esperar a que el subproceso t2 termine
std::cout << "Final value of shared_resource: " << shared_resource << std::endl;
return 0;
}
Salida:
Incremented shared_resource to 1 (count = 3) Incremented shared_resource to 2 (count = 2) Incremented shared_resource to 3 (count = 1) Incremented shared_resource to 4 (count = 0) Incremented shared_resource to 5 (count = 3) Incremented shared_resource to 6 (count = 2) Incremented shared_resource to 7 (count = 1) Incremented shared_resource to 8 (count = 0) Final value of shared_resource: 8
Análisis del código:
Crear
std::recursive_mutexobjeto:std::recursive_mutex rmtx;Es un mutex recursivo que permite que el mismo subproceso obtenga el bloqueo varias veces.Recurso compartido:
int shared_resource = 0;Es un recurso al que acceden varios subprocesos.Función recursiva
recursive_increment:- usa
std::lock_guard<std::recursive_mutex>Sobrermtxpara bloquear. Dado que se usa un mutex recursivo, el mismo subproceso puede obtener el bloqueo varias veces. - mediante
++shared_resourceSe incrementa el valor del recurso compartido. - La función se llama a sí misma recursivamente, lo que demuestra cómo una función recursiva puede trabajar de forma segura con múltiples bloqueos.
- usa
Crear y ejecutar subprocesos:
std::thread t1(recursive_increment, 3);ystd::thread t2(recursive_increment, 3);Se crean dos subprocesos por separado, ambos ejecutaránrecursive_increment(3)。t1.join()yt2.join()Espera a que ambos subprocesos terminen.
Notas
- Asegúrese de desbloquear el mutex después de cada bloqueo para evitar interbloqueos.
- Usar
std::lock_guardostd::unique_lockPuede gestionar automáticamente el bloqueo y desbloqueo del mutex, reduciendo la posibilidad de errores. - Evite llamar a funciones que puedan lanzar excepciones mientras se mantiene el mutex, ya que esto puede provocar un interbloqueo.
<mutex>Es un archivo de cabecera muy importante en la biblioteca estándar de C++. Proporciona mecanismos básicos de sincronización para la programación multihilo. Mediante el uso de mutex, podemos garantizar que el acceso a los recursos compartidos sea seguro, evitando así problemas de carrera de datos e inconsistencias.
Más explicaciones
1. std::mutex
Proporciona un mutex básico, garantizando que solo un subproceso pueda acceder a un recurso compartido al mismo tiempo.
Ejemplo
std::mutex mtx;
void thread_function() {
std::lock_guard<std::mutex> lock(mtx);
// acceder al recurso compartido
}
2. std::recursive_mutex
Permite que el mismo subproceso obtenga el bloqueo varias veces sin causar interbloqueos, lo que es especialmente útil para funciones recursivas.
Ejemplo
std::recursive_mutex rmtx;
void recursive_function(int count) {
if (count <= 0) return;
std::lock_guard<std::recursive_mutex> lock(rmtx);
// llamada recursiva
recursive_function(count - 1);
}
3. std::timed_mutex
Proporciona un mutex con tiempo de espera, que permite establecer un tiempo límite al intentar obtener el bloqueo.
Ejemplo
#include <chrono>
std::timed_mutex tm;
void try_lock_for_example() {
if (tm.try_lock_for(std::chrono::seconds(1))) {
// bloqueo obtenido con éxito
tm.unlock();
} else {
// fallo al obtener el bloqueo
}
}
4. std::recursive_timed_mutex
Hereda de std::timed_mutex, permite que el mismo subproceso obtenga el bloqueo varias veces y admite funciones de temporización.
Ejemplo
#include <chrono>
std::recursive_timed_mutex rtm;
void recursive_timed_function(int count) {
if (count <= 0) return;
if (rtm.try_lock_for(std::chrono::seconds(1))) {
// bloqueo obtenido con éxito
recursive_timed_function(count - 1);
rtm.unlock();
} else {
// fallo al obtener el bloqueo
}
}
5. std::lock_guard
Un envoltorio que gestiona automáticamente el bloqueo de std::mutex, utiliza el estilo RAII y garantiza que el bloqueo se libere automáticamente al final del ámbito.
Ejemplo
std::mutex mtx;
void function() {
std::lock_guard<std::mutex> lock(mtx);
// acceder al recurso compartido
}
6. std::unique_lock
Proporciona una gestión de bloqueo más flexible que std::lock_guard, permite liberar y volver a adquirir el bloqueo manualmente, y también admite bloqueo con tiempo de espera.
Ejemplo
#include <chrono>
std::mutex mtx;
void function() {
std::unique_lock<std::mutex> lock(mtx);
// acceder al recurso compartido
// se puede liberar el bloqueo manualmente
lock.unlock();
// se puede volver a adquirir el bloqueo
lock.lock();
// se puede realizar un bloqueo con tiempo de espera
if (lock.try_lock_for(std::chrono::seconds(1))) {
// bloqueo obtenido con éxito
}
}
7. std::adopt_lock_t
Tipo de flag, utilizado para especificar que std::unique_lock adopte un bloqueo existente.
Ejemplo
std::unique_lock<std::mutex> lock(mtx, std::adopt_lock);
8. std::defer_lock_t
Función: tipo de flag, utilizado para bloqueo diferido; no bloquea durante la inicialización.
Ejemplo
std::unique_lock<std::mutex> lock(mtx, std::defer_lock);
// se puede llamar a lock.lock() en algún momento posterior
9. std::try_to_lock_t
Tipo de flag, utilizado para intentar bloquear sin bloquearse.
Ejemplo
std::unique_lock<std::mutex> lock1(mtx1, std::try_to_lock);
std::unique_lock<std::mutex> lock2(mtx2, std::try_to_lock);
if (lock1 && lock2) {
// se obtuvieron dos bloqueos con éxito
}