...te lo explicaré: yo soy un POSIX thread y tu un C11 thread... |
¿Cómo se han realizado los nuevos C11 Threads? Vale, han tomado todas las funciones y variables que componen los POSIX Threads y le han cambiado el nombre (y tengo que admitir que los nuevos son más simples); además, en algunos casos (pocos, afortunadamente), han cambiado los tipos de los códigos de retorno y de los argumentos de las funciones. Punto. ¿Brillante? No exactamente, diría, y nada que ver con la brillante solución utilizada en C++11. ¿Razones para usar esta nueva versión? Cero, diría, y todavía no he expuesto el problema principal...
De todos modos, he reescrito el ejemplo del último post usando los C11 Threads. ¡Vamos con el código!
#include <stdio.h> #include <threads.h> #include <string.h> #include <unistd.h> // creo un nuevo tipo parar pasar datos a los thread typedef struct _tdata { int index; // thread index int *comdata; // dato común a los thread mtx_t *lock; // mutex común a los thread } tdata; // prototipos locales int tMyThread(void *arg); // función main() int main(int argc, char* argv[]) { int error; // init mutex mtx_t lock; if ((error = mtx_init(&lock, mtx_plain)) != thrd_success) { printf("%s: no puedo crear el mutex (error=%d)\n", argv[0], error); return 1; } // init threads thrd_t tid[2]; tdata data[2]; int comdata = 0; for (int i = 0; i < 2; i++) { // set data del thread y crea el thread data[i].index = i; data[i].comdata = &comdata; data[i].lock = &lock; if ((error = thrd_create(&tid[i], tMyThread, &data[i])) != thrd_success) printf("%s: no puedo crear el thread %d (error=%d)\n", argv[0], i, error); } // join threads y borra mutex thrd_join(tid[0], NULL); thrd_join(tid[1], NULL); mtx_destroy(&lock); // exit printf("%s: thread acabados: comdata=%d\n", argv[0], comdata); return 0; } // thread routine int tMyThread(void *arg) { // obtengo los datos del thread con un cast (tdata*) de (void*) arg tdata *data = (tdata *)arg; // thread loop printf("thread %d comenzado\n", data->index); int i = 0; for (;;) { // lock mutex mtx_lock(data->lock); // incrementa comdata (*data->comdata)++; // unlock mutex mtx_unlock(data->lock); // test counter parar eventual salida del loop if (++i >= 100) { // sale del loop break; } // thread sleep (10 ms) usleep(10000); } // el thread sale printf("thread %d acabado\n", data->index); return 0; }
Como se puede ver el código es prácticamente lo mismo, me he limitado a utilizar las nuevas funciones en lugar de las viejas (por ejemplo, thrd_create() en lugar de pthread_create()), he utilizado los nuevos tipos (por ejemplo, mtx_t en lugar de pthread_mutex_t) y he ligeramente modificado el test de los valores de retorno: pocas diferencias, tengo que decir, y, en algunos casos, a peor: por ejemplo, ha desaparecido el parámetro attr de pthread_create(), que (por simplicidad) en el último ejemplo había dejado a NULL, pero que a veces puede ser útil (leer el manual de pthread_create() para darse cuenta). Sin embargo, se podría decir (sin ser demasiados exigentes) que la nueva interfaz no nos ofrece ninguna ventaja sustancial, pero ni siquiera un deterioro decisivo, por lo que también se podría utiliza (de gustibus).
Pero hay un problema: parece que los C11 Threads no son considerados una prioridad para los que escriben los compiladores y las libc, por lo que actualmente es difícil compilar/ejecutar un programa como el que he enseñado. Incluso nuestro amado GCC (que suele ser el primero en dar soporte a las últimas novedades) no soporta los nuevos thread (en realidad debido a la falta de integración en la glibc). Por lo tanto, si realmente deseáis usarlos a toda costa, tendréis que esperar a que algún compilador/librería proporcione el soporte completo o bien, por ejemplo, utilizar la librería c11threads, que no es más que un wrapper que simula los C11 Threads utilizando los POSIX Threads.
Yo, al final, he compilado el ejemplo usando lo que (creo) es la solución más interesante actualmente disponible: he instalado en mi sistema la musl libc que es una libc alternativa a la glibc, y tiene un wrapper para GCC (musl-gcc): musl proporciona (en Linux) el soporte completo a C11, thread incluidos. Una vez compilado, el programa se comporta correctamente, como se puede ver a continuación:
aldo@ao-linux-nb:~/blogtest$ musl-gcc c11thread.c -o c11thread aldo@ao-linux-nb:~/blogtest$ ./c11thread thread 0 comenzado thread 1 comenzado thread 1 acabado thread 0 acabado ./c11thread: thread acabados: comdata=200
¿Pero vale la pena hacer este cambio? No, por mi parte seguiré utilizando los POSIX Threads, que utilizo desde hace años y siguen siendo la referencia de excelencia. Y una última consideración: independientemente de lo que estamos utilizando (C11/C++11 threads) es muy probable que, por debajo, haya los POSIX Threads (esto es cierto en muchas implementaciones). Y si cuando compiláis tenéis que añadir el flag -pthread entonces la duda se convierte en una certeza, ya que con este flag vais a usar libpthread o sea la librería POSIX Threads. Sorpresa, sorpresa...
¡Hasta el próximo post!