Estoy tratando de usar hilos (por primera vez) en una aplicación GCC C que funciona bien en modo no roscado. Cuando lo ejecuto, algunos subprocesos dan resultados que son todos cero en lugar de las respuestas requeridas (que sé para fines de verificación), pero los subprocesos que dan ceros no son los mismos cada vez que lo ejecuto. Los que dan respuestas distintas de cero son correctos, por lo que el código parece funcionar bien como tal. Me pregunto si alguien puede señalar áreas en las que podría tener algo que no sea seguro para subprocesos.¿Pueden los hilos escribir en diferentes elementos del mismo conjunto de estructuras sin bloqueo?
Pienso que puede deberse a la forma en que recopilo resultados o a la asignación de memoria. Utilizo malloc y libre, pero en otros lugares de StackOverflow veo que GCC malloc se considera seguro para subprocesos si se vincula con -lpthread (que soy obra). Nada usa variables globales/estáticas: todo se pasa como argumentos de funciones.
Para pasar los resultados a main, mi rutina con hilos utiliza una matriz de estructuras. Cada hilo escribe en un elemento distinto de esta matriz, por lo que no intentan escribir en la misma memoria. ¿Quizás necesite usar algún tipo de bloqueo al escribir resultados aunque no vayan al mismo elemento de la matriz de estructura?
que siguieron la receta para el código de roscado aquí: https://computing.llnl.gov/tutorials/pthreads/#Abstract
Puedo adjuntar (simplificado) extractos de código en caso de que esto da ninguna pista (que puede haber omitido/algo modificado de forma incorrecta, pero no estoy pidiendo que cualquiera pueda detectar errores, solo la metodología general).
typedef struct p_struct { /* used for communicating results back to main */
int given[CELLS];
int type;
int status;
/*... etc */
} puzstru;
typedef struct params_struct { /* used for calling generate function using threads */
long seed;
char *text;
puzzle *puzzp;
bool unique;
int required;
} paramstru;
/* ========================================================================================== */
void *myfunc(void *spv) /* calling routine for use by threads */
{
paramstru *sp=(paramstru *)spv;
generate(sp->seed, sp->text, sp->puzzp, sp->unique, sp->required);
pthread_exit((void*) spv);
}
/* ========================================================================================== */
int generate(long seed, char *text, puzstru *puzzp, bool unique, int required)
{
/* working code , also uses malloc and free,
puts results in the element of a structure array pointed to by "puzzp",
which is different for each thread
(see calling routine below : params->puzzp=puz+thr;)
extract as follows: */
puzzp->given[ix]=calcgiven[ix];
puzzp->type=1;
puzzp->status=1;
/* ... etc */
}
/* ========================================================================================== */
int main(int argc, char* argv[])
{
pthread_t thread[NUM_THREADS];
pthread_attr_t threadattr;
int thr,threadretcode;
void *threadstatus;
paramstru params[1];
/* ....... ETC */
/* set up params structure for function calling parameters */
params->text=mytext;
params->unique=TRUE;
params->required=1;
/* Initialize and set thread detached attribute */
pthread_attr_init(&threadattr);
pthread_attr_setdetachstate(&threadattr, PTHREAD_CREATE_JOINABLE);
for(thr=0; thr<NUM_THREADS; thr++)
{
printf("Main: creating thread %d\n", thr);
params->seed=ran_arr_next(startingseeds);
params->puzzp=puz+thr;
threadretcode = pthread_create(&thread[thr], &threadattr, myfunc, (void *)params);
if (threadretcode)
{
printf("ERROR; return code from pthread_create() is %d\n", threadretcode);
exit(-1);
}
}
/* Free thread attribute and wait for the other threads */
pthread_attr_destroy(&threadattr);
for(thr=0; thr<NUM_THREADS; thr++)
{
threadretcode = pthread_join(thread[thr], &threadstatus);
if (threadretcode)
{
printf("ERROR; return code from pthread_join() is %d\n", threadretcode);
exit(-1);
}
printf("Main: completed join with thread %d having a status of %ld\n",thr,(long)threadstatus);
}
/* non-threaded code, print results etc ............. */
free(startingseeds);
free(puz);
printf("Main: program completed. Exiting.\n");
pthread_exit(NULL);
}
Para el beneficio de otros leyendo esto - todas las respuestas eran correctas, y la respuesta a la pregunta en el título es sí, las discusiones pueden escribir de forma segura a los diferentes elementos de la misma matriz de estructuras, mi problema era en la rutina de llamada - el siguiente es el fragmento de código modificado (ahora funciona bien):
paramstru params[NUM_THREADS];
for(thr=0; thr<NUM_THREADS; thr++)
{
printf("Main: creating thread %d\n", thr);
/* set up params structure for function calling parameters */
params[thr].text=mytext;
params[thr].unique=TRUE;
params[thr].required=1;
params[thr].seed=ran_arr_next(startingseeds);
params[thr].puzzp=puz+thr;
threadretcode = pthread_create(&thread[thr], &threadattr, myfunc, (void *)¶ms[thr]);
if (threadretcode)
{
printf("ERROR; return code from pthread_create() is %d\n", threadretcode);
exit(-1);
}
}
Hmm. ¿Dónde se declara puz variable? ¿Es de tipo 'rompecabezas *'? ¿Cómo se calcula 'ix'? Supongo que su problema está en el bloque que marcó como "código de trabajo". ;) –