pthread_create y pasando un número entero como último argumento

8 minutos de lectura

avatar de usuario
Degradado

tengo las siguientes funciones:

void *foo(void *i) {
    int a = (int) i;
}

int main() {
    pthread_t thread;
    int i;
    pthread_create(&thread, 0, foo, (void *) i);
}

En la compilación, hay algunos errores sobre la conversión ((void *) i y int a = (int) i). ¿Cómo puedo pasar un número entero como el último argumento de pthread_create ¿adecuadamente?

avatar de usuario
hombre cuervo

Sobre la base de la respuesta de szx (así que dele el crédito), así es como funcionaría en su for lazo:

void *foo(void *i) {
    int a = *((int *) i);
    free(i);
}

int main() {
    pthread_t thread;
    for ( int i = 0; i < 10; ++1 ) {
        int *arg = malloc(sizeof(*arg));
        if ( arg == NULL ) {
            fprintf(stderr, "Couldn't allocate memory for thread arg.\n");
            exit(EXIT_FAILURE);
        }

        *arg = i;
        pthread_create(&thread, 0, foo, arg);
    }

    /*  Wait for threads, etc  */

    return 0;
}

En cada iteración del ciclo, está asignando nueva memoria, cada una con una dirección diferente, por lo que lo que se pasa a pthread_create() en cada iteración es diferente, por lo que ninguno de sus subprocesos termina intentando acceder a la misma memoria y no tiene ningún problema de seguridad de subprocesos de la forma en que lo haría si simplemente pasara la dirección de i. En este caso, también podría configurar una matriz y pasar las direcciones de los elementos.

  • Me sale esto cuando compilo: error: invalid conversion from ‘void*’ to ‘int*’ en esta línea: int *arg = malloc(sizeof(*arg));. Debe poner (int *) antes de malloc.

    – Degradado

    8 de octubre de 2013 a las 0:35


  • @Gradient: Debe compilar esto como C++. El elenco no es necesario en C y, en mi opinión, no debería incluirse.

    – Hombre cuervo

    8 de octubre de 2013 a las 0:38

  • Yo creo la declaración int *arg = malloc(sizeof(*arg)); debería ser en cambio int *arg = malloc(sizeof(arg));. Aquí funciona porque un puntero en la mayoría de las máquinas es del tamaño de int. Sin embargo, puede fallar para otros tipos de datos o incluso tipos personalizados. Corrígeme si me equivoco aquí

    – HighOnMeat

    11 de febrero de 2015 a las 15:36


  • @Pegasus: estás equivocado. Aquí desea asignar la cantidad correcta de espacio para un intno para un puntero a int. Su alternativa haría lo último, por lo que en realidad es su sugerencia que solo funcionaría si los punteros y ints son del mismo tamaño.

    – Hombre cuervo

    11 de febrero de 2015 a las 17:48

  • @Pegasus: No, asigna espacio para un int. int * arg = malloc(sizeof(&arg)); asignaría espacio para un puntero a puntero a int. Si “no está convencido”, entonces simplemente está confundido acerca de la sintaxis básica de C.

    – Hombre cuervo

    12 de febrero de 2015 a las 11:57


Puede asignar un int en el montón y pasarlo a pthread_create(). Luego puede desasignarlo en su función de hilo:

void *foo(void *i) {
    int a = *((int *) i);
    free(i);
}

int main() {
    pthread_t thread;
    int *i = malloc(sizeof(*i));
    pthread_create(&thread, 0, foo, (void *) i);
}

  • Como dije en otra respuesta: no estoy seguro de que esta solución funcione. Cuando llega el primer hilo int a = *((int *) i)el bucle for podría haber cambiado el valor de i. Por lo tanto, cuando el primer subproceso intenta inicializar a, no leería el valor correcto. ¿O tal vez estoy confundido con el concepto de hilos?

    – Degradado

    7 oct 2013 a las 22:46

  • @Gradient: su otro comentario fue correcto, pero eso no es lo que está sucediendo aquí. Esta solución asigna memoria nueva para el argumento de cada subproceso (o, al menos, si lo coloca en un ciclo, asignaría memoria nueva en cada ciclo) para que cada subproceso obtenga un objeto diferente y no haya dos subprocesos que intenten acceder al mismo recuerdo. Está entendiendo bien el concepto de subprocesos, esta solución simplemente no presenta el problema que mencionó. Creo que esta solución se mejoraría al mostrar cómo funcionaría en un bucle.

    – Hombre cuervo

    7 oct 2013 a las 22:48


avatar de usuario
PÁGINAS

Debe enviar la dirección de i (en lugar del valor de i como lo hace ahora) en el último argumento de pthread_create().

pthread_create(&thread, 0, foo, (void *) &i);
                                         ^  is missing

Y el casting también está mal en tu función. Debería ser:

int a = *((int*) i);
  1. Si tienes la intención de leer el valor, también debe inicializar i a algún valor en main() ya que ahora no está inicializado.

2 Utilice la definición adecuada para main():

 int main(void) 

o int main(int argc, char *argv[]) o su equivalente.

  • ¿Funciona eso si i se declara en un for ¿lazo? Por ejemplo for (int i=0;...;...) ¿O debería declararlo ante el for ¿lazo?

    – Degradado

    7 oct 2013 a las 21:13

  • Declarar en un bucle for no es diferente y funcionaría de la misma manera. Pero la declaración de variables en el bucle for solo se permite en el modo C99 (o posterior). Tendrías que compilar con -std=c99 por ejemplo, junto con otras opciones del compilador.

    – PP

    7 oct 2013 a las 21:22

  • Además, si lo hago &iluego le doy al hilo la dirección de la variable i. Si creo otros hilos, no lo haría i ser compartido entre todos los hilos? Ese no sería el comportamiento deseado.

    – Degradado

    7 oct 2013 a las 21:26


  • No estoy seguro de que la segunda solución funcione. Para cuando llega el primer hilo int a = *((int *) i)el bucle for podría haber cambiado el valor de i. Por lo tanto, cuando el primer subproceso intenta inicializar ano leería el valor correcto.

    – Degradado

    7 oct 2013 a las 22:06

  • @Gradient: tiene razón, no se garantiza que pasar la dirección de la variable de bucle sea seguro. Los resultados de lanzar un int a un puntero y viceversa están definidos por la implementación, por lo que esta tampoco es una gran solución. Utilizando malloc() Es la mejor manera.

    – Hombre cuervo

    7 oct 2013 a las 22:47

avatar de usuario
Jerska

Pregunta anterior, pero me enfrenté al mismo problema hoy y decidí no seguir este camino. Mi aplicación era realmente sobre el rendimiento, así que elegí tener este conjunto de ints declarado estáticamente.

Dado que no conozco muchas aplicaciones en las que su pthread_join / pthread_cancel está en otro ámbito que el suyo pthread_createelegí de esta manera :

#define NB_THREADS 4

void *job(void *_i) {
  unsigned int i = *((unsigned int *) _i);
}

int main () {
  unsigned int ints[NB_THREADS];
  pthread_t    threads[NB_THREADS];
  for (unsigned int i = 0; i < NB_THREADS; ++i) {
    ints[i] = i;
    pthread_create(&threads[i], NULL, job, &ints[i]);
  }
}

Lo encuentro más elegante, más eficiente y no tienes que preocuparte por liberar ya que solo vive en este ámbito.

Si bien esta es una pregunta antigua, falta una opción cuando todo lo que necesita es pasar un número entero positivo como un descriptor: puede pasarlo directamente como la dirección, aunque es un truco, funciona bien y evita asignar nada 🙂

NOTA: el tamaño del número entero debe coincidir con el tamaño de un puntero en su sistema operativo, pero hoy en día la mayoría de los sistemas son nativos de 64 bits.

#include <pthread.h>
#include <inttypes.h>
#include <stdio.h>

void *_thread_loop(void *p)
{
  uint64_t n = (uint64_t)p;

  printf("received %llu\n", n);

  return NULL;
}



int main(int argc, char const *argv[])
{
  pthread_t read_thread_id;
  uint64_t n = 42;
  pthread_create(&read_thread_id, NULL, _thread_loop, (void *)n);

  pthread_join(read_thread_id, NULL);
  return 0;
}

  • De acuerdo con esta respuesta, el resultado de este truco está definido por la implementación y (en términos generales) no es portátil.

    – tonysdg

    16 de enero de 2018 a las 13:54

  • No estoy completamente seguro de que la respuesta que vincula realmente signifique eso, dice que el resultado puede no apuntar a ningún dato válido y eso es obvio (el puntero nunca será desreferenciado) pero hasta ahora nunca tuve problemas para hacerlo, no he probado Sin embargo, arquitecturas exóticas me funcionaron en linux en x86 y am64, así como en arm (en una raspberrypi).

    – Schmurfy

    28 de enero de 2018 a las 19:13

  • Oh, dudo mucho que alguna vez tengas problemas con él a menos que estés corriendo en un De Verdad arquitectura exótica 🙂 Y este truco me ha funcionado en más de una ocasión. ¡Solo estoy publicando para completar!

    – tonysdg

    30 de enero de 2018 a las 0:08

  • De acuerdo con esta respuesta, el resultado de este truco está definido por la implementación y (en términos generales) no es portátil.

    – tonysdg

    16 de enero de 2018 a las 13:54

  • No estoy completamente seguro de que la respuesta que vincula realmente signifique eso, dice que el resultado puede no apuntar a ningún dato válido y eso es obvio (el puntero nunca será desreferenciado) pero hasta ahora nunca tuve problemas para hacerlo, no he probado Sin embargo, arquitecturas exóticas me funcionaron en linux en x86 y am64, así como en arm (en una raspberrypi).

    – Schmurfy

    28 de enero de 2018 a las 19:13

  • Oh, dudo mucho que alguna vez tengas problemas con él a menos que estés corriendo en un De Verdad arquitectura exótica 🙂 Y este truco me ha funcionado en más de una ocasión. ¡Solo estoy publicando para completar!

    – tonysdg

    30 de enero de 2018 a las 0:08

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad