2013-06-19 4 views
0

J'essaye d'émuler un mécanisme de rappel en C avec pthreads. Le code que j'ai est ci-dessous:Passer le pointeur de fonction dans une structure avec pthreads

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

struct fopen_struct { 
    char *filename; 
    char *mode; 
    void *(*callback) (FILE *); 
}; 

void *fopen_callback(FILE *); 
void fopen_t(void *(*callback)(FILE *), const char *, const char *); 
void *__fopen_t__(void *); 

void fopen_t(void *(*callback)(FILE *), const char *filename, const char *mode) { 
    struct fopen_struct args; 
    args.filename = filename; 
    args.mode = mode; 
    args.callback = callback; 
    pthread_t thread; 
    pthread_create(&thread, NULL, &__fopen_t__, &args); 
} 

void *__fopen_t__(void *ptr) { 
    struct fopen_struct *args = (struct fopen_struct *)ptr; 
    FILE *result = fopen(args -> filename, args -> mode); 
    args -> callback(result); 
} 

int main() { 
    fopen_t(&fopen_callback, "test.txt", "r"); 
} 

void *fopen_callback(FILE *stream) { 
    if (stream != NULL) 
    printf("Opened file successfully\n"); 
    else 
    printf("Error\n"); 
} 

Cette compile, mais lors de l'exécution, il se termine sans une erreur ou un message à l'écran. Qu'est-ce que je rate?

+0

Votre filetage principal se termine prématurément. – hetepeperfan

+0

Cela semble un peu lourd d'engendrer un thread juste pour exécuter un callback. Si j'étais vous, j'aurais créé un fil dédié à l'exécution du rappel. Lorsqu'un rappel est nécessaire, vous pouvez le placer dans une file d'attente qui sera traitée par le thread de rappel. Bien sûr, si le travail effectué par le callback est trop important (mais il ne devrait pas l'être), générer un thread pour cela est correct :) – Rerito

Répondre

3

Votre thread main se termine avant l'achèvement de __fopen_t__. Donc soit détacher ce fil (fopen_t) en utilisant pthread_detach et faire d'autres choses utiles ou attendre l'achèvement de __fopen_t__ en utilisant pthread_join.

Lorsque vous utilisez pthread_join, votre fopen_t peut ressembler,

void fopen_t(void *(*callback)(FILE *), const char *filename, const char *mode) 
{ 
    struct fopen_struct args; 
    args.filename = filename; 
    args.mode = mode; 
    args.callback = callback; 
    pthread_t thread; 
    pthread_create(&thread, NULL, &__fopen_t__, &args); 
    pthread_join(thread, NULL); // Waiting till the new thread completes 
} 

Référez pages de manuel pthread_detach et pthread_join pour plus de détails. Pour être plus logique selon le commentaire de R .., le code à allocation dynamique est donné ci-dessous.

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

struct fopen_struct { 
    char *filename; 
    char *mode; 
    void *(*callback) (FILE *); 
}; 

void *fopen_callback(FILE *); 
pthread_t* fopen_t(void *(*callback)(FILE *), const char *, const char *); 
void *__fopen_t__(void *); 

// returns pthread_t* to be freed by caller 
pthread_t* fopen_t(void *(*callback)(FILE *), const char *filename, const char *mode) 
{ 

    struct fopen_struct *args = calloc(1, sizeof( struct fopen_struct)); 
    args->filename = filename; 
    args->mode = mode; 
    args->callback = callback; 

    pthread_t *thread = calloc(1, sizeof(pthread_t)); // Need error checks 
    pthread_create(thread, NULL, &__fopen_t__, args); 
    //pthread_join(thread, NULL); // `thread` is returned to caller 

    return thread; 
} 

// takes `struct fopen_struct*` as argument and will be freed 
void *__fopen_t__(void *ptr) { 
    struct fopen_struct *args = (struct fopen_struct *)ptr; 
    FILE *result = fopen(args -> filename, args -> mode); 
    args -> callback(result); 

    free(args); args = NULL; 
    return NULL; 
} 

int main() { 
    pthread_t *th_id = NULL; 

    th_id = fopen_t(&fopen_callback, "test.txt", "r");  // Need error checks 
    pthread_join(*th_id, NULL); // Wait till the __fopen_t__ thread finishes 
    free(th_id); th_id = NULL; 

    return 0; 
} 
+2

Je ne pense pas que 'fopen_t' est supposé attendre que le thread finisse; Tout le problème est qu'il peut fonctionner de manière asynchrone. Au lieu de cela, 'fopen_t' devrait * renvoyer * le' pthread_t' afin que l'appelant puisse attendre plus tard le résultat (le rejoindre). –

+0

@ R..thanks et le même est mis à jour dans la réponse. S'il vous plaît libre de modifier la réponse ci-dessus si nécessaire – VoidPointer

0

Ajouter sleep() dans le bas de la page principale. Peut-être que votre programme a fini avant d'obtenir le résultat.

MENTION COMPLÉMENTAIRE

C'est moyen le plus simple de remarquer ce bug, mais pas de manière correcte. :)

+3

Je ne pense pas que dormir est une bonne façon de synchroniser différents threads. – hetepeperfan

+0

Oui, je sais. C'est la manière la plus simple que @ sankha-narayan-guria remarque son bug. – mattn

+0

Oui, c'est un moyen facile de remarquer le bug, mais c'est une recommandation très néfaste pour quelqu'un de nouveau à threads. À moins d'avoir une compréhension innée magique de la synchronisation, les nouveaux programmeurs de threads adopteront vraisemblablement cette pratique comme un substitut à une synchronisation correcte, ce qui n'est pas le cas. –

Questions connexes