2015-12-30 30 views
1

我正在我的項目中,它是至關重要的,一切正常清理,所有緩衝的日誌消息保存到一個文件等..我打電話退出另一個線程和我正在考慮在程序完全退出之前使用信號量等待主線程中的清理。問題是,當我從退出處理程序調用sem_post並使用atexit註冊時,sem_wait不會立即減少信號量,因此,退出處理程序中的sem_wait將立即退出,因爲信號量大於零。sem_post沒有優先考慮在其他線程sem_wait調用

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

static sem_t sem; 

void *do_work(void *arg) { 
    // if something fails 
    printf("Something failed!\n"); 
    exit(1); 
} 

void exit_handler() { 
    sem_post(&sem); // wake up main thread 
    sem_wait(&sem); // wait for cleanup in main 
    sem_destroy(&sem); 
} 

int main() { 
    pthread_t worker_thread; 

    sem_init(&sem, 0, 0); 
    atexit(exit_handler); 

    pthread_create(&worker_thread, NULL, do_work, NULL); 

    sem_wait(&sem); // block this thread until work is done 

    // simulate some cleanup 
    usleep(1000000); 
    printf("This never gets called!\n"); 

    sem_post(&sem); // destroy semaphore 
} 

這個example演示我的問題。有沒有解決這個問題的方法?我不能把清理放到我的退出處理程序中,因爲我的主函數中有很多本地資源需要在我的實際程序中清理。

+0

@Mat對不起,忘了包括。但它仍然不起作用。 – Linus

+2

我在這裏看不到sem_ *的好處。你不能只使用pthread_join()來等待線程終止,然後進行清理嗎? – Ctx

+0

@Ctx嗯,在這種特殊情況下,我可以。然而,我的實際程序中有很多其他線程需要終止,而其他操作必須在main中完成,因爲我不想過度使用我使用的每個資源的全局變量。 – Linus

回答

1

在調用sem_post()之後,您無法控制哪個線程將從sem_wait()返回。你需要使用兩個信號量:

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

static sem_t wakeupMain; 
static sem_t cleanupDone; 

void *do_work(void *arg) { 
    // if something fails 
    printf("Something failed!\n"); 
    exit(1); 
} 

void exit_handler() { 
    sem_post(&wakeupMain); // wake up main thread 
    sem_wait(&cleanupDone); // wait for cleanup in main 
    sem_destroy(&wakeupMain); 
    sem_destroy(&cleanupDone); 
} 

int main() { 
    pthread_t worker_thread; 

    sem_init(&wakeupMain, 0, 0); 
    sem_init(&cleanupDone, 0, 0); 
    atexit(exit_handler); 

    pthread_create(&worker_thread, NULL, do_work, NULL); 

    sem_wait(&wakeupMain); // block this thread until work is done 

    // simulate some cleanup 
    usleep(1000000); 
    printf("This never gets called!\n"); 

    sem_post(&cleanupDone); // destroy semaphore 
} 
+1

爲了補充說明,*理由*不可能保證另一個線程在atexit處理程序調用它自己的'sem_wait'之前會被喚醒是因爲沒有辦法觀察另一個線程是否正在等待sem_wait '「或」正要調用'sem_wait'「。雖然後者不太可能,但沒有進一步的同步,你不能證明情況並非如此,因此你不能假設它。 –

+0

@R ..確實。即使另一個線程在'sem_wait()'中被阻塞,也不能保證已經運行的調用'sem_post()'的線程不會是通過'sem_wait()'獲取信號量的線程。我甚至敢於猜測這很可能,因爲操作系統的調度程序可以將信號「給」到已經運行的線程,而不必阻塞該線程並執行上下文切換到其他線程。 –

+0

正式地說,如果有現有的服務員,就需要解除阻塞。唯一允許不執行此操作的實現是「asf」規則,並且無法觀察另一個線程是否已在等待或即將等待。 –