¿Cómo puedo pasar el índice de un bucle for como argumento para pthread_create?

6 minutos de lectura

¿Como puedo pasar el indice de un bucle for como
tonio

Estoy usando un bucle for para crear varios subprocesos y pasar el índice i como argumento de la siguiente manera:

pthread_t p[count];
for (int i = 0; i < count; i++){
    pthread_create(&p[i], NULL, &somefunc, (void*)&i);
}

Luego intento recuperar el valor de i:

void *somefunc (void* ptr){
    int id = *(int*)ptr;
}

Sin embargo, noté que a veces, la identificación en los subprocesos tendrá valores superpuestos, lo que sospecho que se debe a que el índice del bucle for se actualiza antes de que el subproceso pueda recuperar el valor (ya que pasé el puntero, en lugar del valor sí mismo). ¿Alguien tiene alguna sugerencia para superar este problema sin ralentizar el bucle for?

Gracias

1647630011 818 ¿Como puedo pasar el indice de un bucle for como
Capitán Obvio

Esto sucede porque una vez que pasa un puntero a i ahora tiene varios subprocesos que utilizan el mismo valor. Esto provoca una carrera de datos porque el primer subproceso se está modificando. i y su segundo hilo espera que nunca cambie. Siempre puede asignar un int temporal y pasarlo a la función de subproceso.

pthread_create(&p[i], NULL, &somefunc, new int(i));

Esto asignará un número entero en el almacenamiento dinámico (montón) y lo inicializará con el valor de i. A continuación, se pasará un puntero al entero recién asignado a la función de subproceso.

Luego, en la función de subproceso, puede tomar el valor pasado como ya lo hizo y luego eliminar el objeto int.

void *somefunc (void* ptr){
    int id = *(int*)ptr;
    delete (int*)ptr;
}

[Suggestion: Avoid C style casts.]

Como han dicho otros, está pasando un puntero a un objeto que está siendo modificado por otro hilo (el padre) y accede a él sin ninguna sincronización. Esto es un error.

Hay al menos 3 soluciones:

  1. Asignar (a través de new en C++ o malloc en C) espacio para un solo int, y haga que el nuevo subproceso se encargue de liberarlo. Esta es probablemente la peor solución porque ahora tiene que manejar un caso de falla adicional (falla en la asignación) y, por lo tanto, complica y desordena su código.

  2. Echa el entero a void * y vuelta Esto seguramente funcionará en cualquier sistema POSIX del mundo real, pero no está “garantizado” que funcione, y quizás lo que es más molesto, puede generar advertencias. Puede evitar las advertencias con un lanzamiento intermedio a través uintptr_t.

  3. En lugar de pasar un índice, pasa una dirección:

    pthread_create(&p[i]NULL, &algunafunción, &p[i]);

Luego, la función de inicio puede recuperar el índice (si lo necesita para algo) restando p:

int id = (pthread_t *)ptr - p;

  • +1 para enumerar opciones con pros/contras. 3 es inteligente, pero las funciones lanzadas pueden carecer de acceso a p (Iría tan lejos como para decir que normalmente no deberían saber o depender de tales detalles de su creador).

    – Tony Delroy

    19 de abril de 2013 a las 3:13


  • Estoy de acuerdo, pero (3) parece aplicarse en el caso de OP. Por cierto, si el número de índices es pequeño, puede usar el mismo principio pero con una matriz ficticia: static const char dummy[100]; y luego pasando &dummy[i] a la función de inicio como una forma indirecta de pasar i.

    – R.. GitHub DEJA DE AYUDAR A ICE

    19 de abril de 2013 a las 3:39


Has hecho esto un poco demasiado complicado:

for (int i = 0; i < count; i++){
    pthread_create(&p[i], NULL, &somefunc, (void*)&i);

Solo desea pasar el valor, no un puntero, así que pase (void*)i. Tal como está, está pasando a cada subproceso un puntero a i que tiene problemas:

  • i probablemente habrá dejado el alcance para cuando el subproceso intente leer desde su dirección; alguna otra variable podría estar allí en su lugar o la memoria podría estar sin usar con quién sabe qué queda en ella
  • la siguiente iteración en el bucle sobrescribirá el valor de todos modos, lo que significa que es probable que todos los subprocesos vean el valor “contar” cuando eliminen la referencia del puntero, si no hubiera sido golpeado como se indicó anteriormente, y excepto en casos raros donde el lanzamiento el hilo se suspende durante el bucle, lo que permite que un hilo que generó lea algo antes i valor

Entonces:

for (int i = 0; i < count; i++){
    pthread_create(&p[i], NULL, &somefunc, (void*)i);

...
void *somefunc (void* id_cast_to_voidptr){
    int id = (int)id_cast_to_voidptr;
}

  • Recibo una serie de advertencias/errores de conversión tanto para (void*)i como para (int)id_cast_to_voidptr.

    – Tony

    19 de abril de 2013 a las 2:37

  • Sin embargo, use un yeso intermedio uintptr_t para deshacerse de la advertencia.

    – R.. GitHub DEJA DE AYUDAR A ICE

    19 de abril de 2013 a las 2:45

  • Esto parece ser lo mismo. No hay diferencia para inicializar la variable en el ciclo for. Esto definitivamente funciona para JS pero no para C

    – Carlos Emiliano Castro Trejo

    23 de enero de 2021 a las 22:20

  • @CarlosEmilianoCastroTrejo: “Esto parece ser lo mismo”.qué parece ser lo mismo que qué? Si quiere decir que el fragmento de código superior es el mismo que en la pregunta, sí, deliberadamente, para que pueda tenerlo allí como referencia fácil mientras explico por qué no funciona. “No hay diferencia para inicializar la variable en el ciclo for”. – correcto – no había nada malo con el bucle for, por lo que no hay razón para hacer nada diferente. Nada de lo que dices tiene sentido. Por supuesto que funciona para C.

    – Tony Delroy

    24 de enero de 2021 a las 3:00

Creo que la mejor respuesta es declarar una matriz de argumentos al principio, con el mismo tamaño que la cantidad de subprocesos que pretende crear.

De esa forma, esos valores nunca se sobrescriben ni quedan en condición de carrera.

int args[count];

for (int i = 0; i < count; i++){
     args[i]=i;
     pthread_create(&p[i], NULL, &somefunc, (void*)args[i]);
}

Puede declarar una matriz de identificación de tareas al comienzo de la función principal. Cada vez que crea el hilo, puede enviar el índice de esa matriz para evitar que se sobrescriba el parámetro.

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#define NUM_THREADS 8

void *PrintHello(void *threadid)
{
   long taskid;
   sleep(1);
   taskid = (long) threadid;
   printf("Hello from thread %ld\n", taskid);
   pthread_exit(NULL);
}

int main(int argc, char *argv[])
{
   pthread_t threads[NUM_THREADS];
   long taskids[NUM_THREADS];
   int rc;

   for(long t=0;t<NUM_THREADS;t++) {
      printf("Creating thread %ld\n", t);
      taskids
      rc = pthread_create(&threads
      
      if (rc) {
         printf("ERROR; return code from pthread_create() is %d\n", rc);
         exit(-1);
         }
   }

   pthread_exit(NULL);
}

  • No es de extrañar que no entendieras y rechazaras mi respuesta, ni siquiera entiendes la tuya. (void *) taskids

    – Tony Delroy

    24 de enero de 2021 a las 3:07


  • No es de extrañar que no entendieras y rechazaras mi respuesta, ni siquiera entiendes la tuya. (void *) taskids

    – Tony Delroy

    24 de enero de 2021 a las 3:07


¿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