Fundamentos

Hilos con std::thread

Un hilo es un flujo de ejecución independiente dentro de un proceso. En una planta industrial, cada dispositivo que debe actuar a la vez (una cinta, una prensa, un robot, un regulador PI) se programa naturalmente como un hilo. Esta página cubre la mecánica básica; la coordinación entre hilos llega con semáforos, mutex y colas.

Por qué multihilo en control de instalaciones

Una instalación tiene actividad genuinamente simultánea: la cinta de entrada avanza mientras la prensa estampa mientras el regulador de temperatura muestrea. Codificarlo como una máquina de estados secuencial obliga a intercalar manualmente cada actividad (sondear sensores por turnos), lo que complica el código y desperdicia tiempo de respuesta. Con un hilo por actividad:

📘 Nota

En los ejercicios clásicos de SCTR verás la construcción concurrente { h1(); h2(); … }. Es pseudocódigo que significa «lanza estos hilos a la vez». En C++ real se traduce a varios std::thread o std::jthread lanzados desde main.

Tu primer hilo

#include <iostream>
#include <thread>

void vigilaCinta() {
    std::cout << "Hilo de la cinta en marcha\n";
}

int main() {
    std::thread t(vigilaCinta);  // lanza el hilo; empieza a ejecutarse YA
    std::cout << "Hilo principal\n";
    t.join();                    // espera a que t termine antes de salir
}

Constructores clave de std::thread:

🔥 Regla de oro

Si un std::thread se destruye sin haber hecho join() ni detach(), el programa llama a std::terminate() y muere. Todo hilo lanzado debe recogerse.

Pasar datos a un hilo

El patrón habitual en planta: un mismo código de hilo para varios dispositivos, parametrizado por índice.

#include <iostream>
#include <thread>
#include <vector>

void hMaquina(int nMaquina) {          // un hilo por máquina
    std::cout << "Máquina " << nMaquina << " trabajando\n";
}

int main() {
    std::vector<std::thread> hilos;
    for (int i = 0; i < 5; i++)
        hilos.emplace_back(hMaquina, i);   // lanza 5 hilos
    for (auto& h : hilos)
        h.join();                          // recoge los 5
}

Para pasar una referencia (y que el hilo escriba en una variable del que llama) hay que pedirlo explícitamente con std::ref:

void mide(double& resultado);   // el hilo escribe en resultado

double temperatura;
std::thread t(mide, std::ref(temperatura));
t.join();
// aquí temperatura ya tiene el valor medido

std::jthread: hilos con parada cooperativa (C++20)

std::jthread hace join() automáticamente al destruirse y añade una señal de parada (stop_token) que el hilo puede consultar. Es la herramienta adecuada para la parada ordenada de una instalación (ver Supervisión y parada).

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

void hCinta(std::stop_token parada) {      // recibe la señal de parada
    while (!parada.stop_requested()) {     // trabaja mientras nadie pida parar
        std::cout << "cinta en marcha\n";
        std::this_thread::sleep_for(std::chrono::milliseconds(500));
    }
    std::cout << "cinta detenida de forma ordenada\n";
}

int main() {
    std::jthread cinta(hCinta);            // se une sola al salir del ámbito
    std::this_thread::sleep_for(std::chrono::seconds(3));
    cinta.request_stop();                  // pide la parada; hCinta lo verá
}

El hilo que llama: this_thread

FunciónEfecto
std::this_thread::sleep_for(d)Duerme al hilo actual durante la duración d (p. ej. std::chrono::milliseconds(10)).
std::this_thread::sleep_until(t)Duerme hasta el instante absoluto t; útil para períodos de muestreo exactos.
std::this_thread::yield()Sugiere al planificador ceder el turno a otro hilo.
std::this_thread::get_id()Identificador del hilo actual; práctico para trazas.
std::thread::hardware_concurrency()Número de hilos hardware disponibles (orientativo).

Cuántos hilos lanzar

En control de instalaciones el número de hilos no lo dicta el número de núcleos, sino el número de actividades concurrentes: uno por dispositivo o estación, más los de servicio (comunicaciones, supervisión, alarmas). Decenas de hilos bloqueados no cuestan apenas CPU; solo memoria. Lo caro es el hilo que consulta en bucle sin bloquearse (busy-wait) — eso se corrige con semáforos y variables de condición, como verás en Semáforos.

💡 Resumen

Un hilo = una actividad continua de la planta. Lanza uno por dispositivo, recoge todos con join() (o usa std::jthread), y nunca dejes un std::thread sin unir.