Biblioteca de multihilo de C++<thread>

C++11 introdujo soporte para multihilo, mediante<thread>la biblioteca, los desarrolladores pueden implementar fácilmente procesamiento paralelo en sus programas.

Este artículo presentará<thread>los conceptos básicos, definiciones, sintaxis y cómo usar la biblioteca para crear y gestionar hilos.

Un hilo es la unidad más pequeña de ejecución de un programa y la unidad más pequeña que el sistema operativo puede programar para la ejecución.

En un programa multihilo, varios hilos pueden ejecutarse en paralelo, mejorando la eficiencia de ejecución del programa.

C++ <thread>Descripción general de la biblioteca

<thread>La biblioteca es parte de la biblioteca estándar de C++ y proporciona las funcionalidades básicas para crear y gestionar hilos. Incluye los siguientes componentes clave:

  • std::thread: representa un hilo, permite crear, iniciar, esperar y destruir hilos.
  • std::this_thread: proporciona algunas funciones miembro estáticas para operar sobre el hilo actual.
  • std::thread::id: identificador único del hilo.

Crear hilos

Para crear un hilo, necesitas instanciarstd::threadla clase y pasar un objeto invocable (función, expresión lambda o función miembro de un objeto) como argumento.

Ejemplo

#include <iostream>
#include <thread>

void print_id(int id) {
    std::cout << "ID: " << id << ", Thread ID: " << std::this_thread::get_id() << std::endl;
}

int main() {
    std::thread t1(print_id, 1);
    std::thread t2(print_id, 2);
}

Iniciar hilo

Crearstd::threadUna vez creado el objeto, el hilo comenzará a ejecutarse inmediatamente. Puedes llamar aljoin()método para esperar a que el hilo termine.

t1.join();
t2.join();

Esperar a que el hilo termine

join()El método bloqueará el hilo actual hasta que el hilo llamado complete su ejecución.

Destruir hilo

Cuando el hilo haya terminado su ejecución, puedes usardetach()el método para separar el hilo, o dejar questd::threadel objeto se destruya automáticamente al salir del ámbito.

t1.detach(); // 线程将继续运行,但无法再被 join o  detach

Ejemplo: uso<thread>Crear computación paralela

A continuación se muestra un ejemplo de computación paralela que usa<thread>la biblioteca, para calcular la suma de dos números.

Ejemplo

#include <iostream>
#include <thread>

int sum = 0;

void add(int a, int b) {
    sum += a + b;
}

int main() {
    int a = 5;
    int b = 10;

    std::thread t1(add, a, b);
    std::thread t2(add, a, b);

    t1.join();
    t2.join();

    std::cout << "Sum: " << sum << std::endl; // Resultado: Sum: 30
}

El resultado es:

Sum: 30

En el siguiente ejemplo, crearemos dos hilos. Cada hilo ejecutará una función simple que imprime un mensaje y duerme durante un tiempo:

Ejemplo

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

// Función simple que se ejecuta en el hilo
void print_message(const std::string& message, int delay) {
    std::this_thread::sleep_for(std::chrono::milliseconds(delay));
    std::cout << message << std::endl;
}

int main() {
    // Crear dos hilos que ejecutan la función print_message
    std::thread t1(print_message, "Hello from thread 1", 1000);
    std::thread t2(print_message, "Hello from thread 2", 500);

    // Esperar a que el hilo t1 termine
    if (t1.joinable()) {
        t1.join();
    }

    // Esperar a que el hilo t2 termine
    if (t2.joinable()) {
        t2.join();
    }

    std::cout << "Main thread finished." << std::endl;

    return 0;
}

El resultado es:

Hello from thread 2
Hello from thread 1
Main thread finished.

Notas

  • Seguridad de hilos: en un entorno multihilo, el acceso a los recursos compartidos debe estar sincronizado para evitar carreras de datos.
  • Ciclo de vida de los hilos: asegúrate de gestionar correctamente los objetos de hilo después de que terminen para evitar fugas de recursos.

Clases y funciones

<thread>La biblioteca contiene una serie de clases y funciones para crear, gestionar y sincronizar hilos.

A continuación se presenta una descripción detallada de la<thread>biblioteca de C++:

Componentes principales

  • std::thread
  • std::mutex
  • std::lock_guard
  • std::unique_lock
  • std::condition_variable
  • std::future y std::promise
  • std::async

std::thread

La clase std::thread se utiliza para crear y gestionar hilos.

Ejemplo

#include <iostream>
#include <thread>

void print_hello() {
    std::cout << "Hello from thread!" << std::endl;
}

int main() {
    std::thread t(print_hello);
    t.join(); // Esperar a que el hilo t termine
    return 0;
}

Métodos importantes

  • join(): espera a que el hilo termine.
  • detach(): coloca el hilo en segundo plano y ya no espera a que termine.
  • joinable(): comprueba si el hilo puede ser unido (join) o separado (detach).

std::mutex

La clase std::mutex se utiliza para sincronizar el acceso a recursos compartidos.

Ejemplo

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

std::mutex mtx; // Crear un objeto mutex global
int shared_resource = 0; // Recurso compartido

// Función del hilo
void increment() {
    std::lock_guard<std::mutex> lock(mtx); // Bloquear para garantizar la seguridad de los hilos
    ++shared_resource;
    std::cout << "Incremented shared_resource to " << shared_resource << std::endl;
    // lock se libera automáticamente cuando lock_guard sale del ámbito
}

int main() {
    std::thread t1(increment);
    std::thread t2(increment);
   
    t1.join(); // Esperar a que el hilo t1 termine
    t2.join(); // Esperar a que el hilo t2 termine

    std::cout << "Final value of shared_resource: " << shared_resource << std::endl;

    return 0;
}

std::lock_guard

std::lock_guard es un administrador de bloqueos de estilo RAII que gestiona automáticamente el ciclo de vida del bloqueo.

Ejemplo

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

std::mutex mtx;

void print_thread_id(int id) {
    std::lock_guard<std::mutex> lock(mtx);
    std::cout << "Thread ID: " << id << std::endl;
}

int main() {
    std::thread t1(print_thread_id, 1);
    std::thread t2(print_thread_id, 2);
    t1.join();
    t2.join();
    return 0;
}

std::unique_lock

std::unique_lock proporciona una gestión de bloqueos más flexible que std::lock_guard.

Ejemplo

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

std::mutex mtx;

void print_thread_id(int id) {
    std::unique_lock<std::mutex> lock(mtx);
    std::cout << "Thread ID: " << id << std::endl;
    lock.unlock(); // Se puede desbloquear manualmente
    // ... otras operaciones
}

int main() {
    std::thread t1(print_thread_id, 1);
    std::thread t2(print_thread_id, 2);
    t1.join();
    t2.join();
    return 0;
}

std::condition_variable

std::condition_variable se utiliza para la espera y notificación entre hilos.

Ejemplo

#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>

std::mutex mtx;
std::condition_variable cv;
bool ready = false;

void print_id(int id) {
    std::unique_lock<std::mutex> lock(mtx);
    cv.wait(lock, []{ return ready; });
    std::cout << "Thread ID: " << id << std::endl;
}

void set_ready() {
    std::unique_lock<std::mutex> lock(mtx);
    ready = true;
    cv.notify_all();
}

int main() {
    std::thread t1(print_id, 1);
    std::thread t2(print_id, 2);
   
    std::this_thread::sleep_for(std::chrono::seconds(1));
    set_ready();
   
    t1.join();
    t2.join();
    return 0;
}

std::future y std::promise

std::future y std::promise se utilizan para transferir resultados entre hilos.

Ejemplo

#include <iostream>
#include <thread>
#include <future>

void calculate_square(std::promise<int> && p, int x) {
    p.set_value(x * x);
}

int main() {
    std::promise<int> p;
    std::future<int> f = p.get_future();
   
    std::thread t(calculate_square, std::move(p), 5);
   
    std::cout << "Square: " << f.get() << std::endl;
   
    t.join();
    return 0;
}

std::async

std::async se utiliza para iniciar tareas asíncronas y devuelve un std::future.

Ejemplo

#include <iostream>
#include <future>

int calculate_square(int x) {
    return x * x;
}

int main() {
    std::future<int> result = std::async(calculate_square, 5);
   
    std::cout << "Square: " << result.get() << std::endl;
   
    return 0;
}

La biblioteca de C++<thread>proporciona a los desarrolladores un potente soporte para multihilo. A través de la introducción de este artículo, deberíamos poder comprender los conceptos básicos de los hilos y aprender a usar<thread>la biblioteca para crear y gestionar hilos. En el desarrollo real, el uso razonable de multihilo puede mejorar significativamente el rendimiento y la capacidad de respuesta del programa.

Otras extensiones