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:
- Cada dispositivo se programa como si tuviera la CPU para él solo: bucle
while (1)con su lógica. - Las esperas largas (un sensor, un temporizador, una comunicación) bloquean solo a su hilo, no a toda la aplicación.
- El sistema operativo reparte la CPU; en sistemas de tiempo real, además, puedes fijar prioridades.
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:
std::thread t(f)— lanza la funciónfen un hilo nuevo.std::thread t(f, a, b)— lanzaf(a, b); los argumentos se copian al hilo nuevo.t.join()— bloquea al hilo que llama hasta quettermina. Después, los recursos del hilo se liberan.t.detach()— se desentiende del hilo (peligroso en aplicaciones serias; evita que el hilo sobreviva a sus datos).
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ón | Efecto |
|---|---|
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.
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.