2013-06-19 40 views
0

我想用pthreads在C中模擬回調機制。我的代碼如下:用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"); 
} 

這個編譯,但是執行時,它會在屏幕上完成而沒有錯誤或消息。我錯過了什麼?

+0

您的主線程在末端完成。 – hetepeperfan

+0

爲了運行一次回調,產生一個線程聽起來有點沉重。如果我是你,我會創建一個專用於回調執行的線程。當需要回調時,您可以將其推送到將由回調線程處理的隊列中。當然,如果回調所做的工作量太大(但不應該),產生一個線程是OK :) – Rerito

回答

3

您的main線程在完成__fopen_t__之前退出。因此,要麼使用pthread_detach分開該線程(fopen_t),並使用pthread_join等其他有用的東西或等待完成__fopen_t__

使用pthread_join,您fopen_t可能看起來像,

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 
} 

參考手冊頁pthread_detachpthread_join的更多細節。


爲了更符合邏輯,根據R ..的評論,動態分配的代碼如下。

#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

我不認爲'fopen_t'應該等待線程完成;關鍵在於它可以異步運行。相反,'fopen_t'應該*返回'pthread_t',以便調用者稍後可以等待結果(加入它)。 –

+0

@ R ..感謝和相同的更新在答案。如有需要請自由修改上述答案 – VoidPointer

0

在主底部添加sleep()。也許你的程序在獲得結果之前完成。

其他意見

這是注意到這個錯誤最簡單的方法,但不是正確的方法。 :)

+3

我不認爲睡眠是同步不同線程的好方法。 – hetepeperfan

+0

是的,我知道。這是@ sankha-narayan-guria注意到他的錯誤的最簡單方法。 – mattn

+0

是的,這是一個很容易注意到這個錯誤的方法,但對於新來的線程來說,這是一個非常有害的建議。除非他們具有某種天生的同步理解,否則新的線程程序員可能會採用這種做法替代正確的同步,但事實並非如此。 –