2011-01-11 48 views
0

嗨 當我運行下面的代碼時,我發現信號線程在另一個線程開始之前會繼續運行很長時間......爲什麼會這樣呢?是不是被喚醒的線程應該在信號發佈者釋放鎖後立即運行?或者OS需要很長時間才能將睡眠線程放回就緒隊列?cond.signal或lock.release有問題嗎?

#include pthread.h 

#include stdio.h 

#include stdlib.h 

void stupidfunction1(void *arg); 

void stupidfunction2(void *arg); 

pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER; 

pthread_cond_t cond1 = PTHREAD_COND_INITIALIZER; 

int thread1count,thread2count; 

int thread1waiting = 0; 

int thread2waiting = 0; 

void main() 

{ 
    printf("Hello World\n"); 

    pthread_t thread1,thread2; 

    int i; 

    thread1count = 0; 

    thread2count = 0; 


    i = pthread_create(&thread1,NULL,&stupidfunction1,NULL); 

    i = pthread_create(&thread2,NULL,&stupidfunction2,NULL); 

    pthread_join(thread1,NULL); 

    pthread_join(thread2,NULL); 

    printf("Done with everythinh"); 


} 


void stupidfunction1(void *arg) 

{ 
int i = 0; 

    for(i = 0;i<50;i++) 
    { 

    thread1count++; 

    pthread_mutex_lock(&mutex1); 

    if((thread1count-thread2count)>5) 

    { 
      thread1waiting = 1; 

       printf("thread1 waiting \n");  

       pthread_cond_wait(&cond1,&mutex1); 

       thread1waiting = 0; 
    } 

     else if((thread2waiting == 1) && abs(thread1count-thread2count)<1) 

    { 

     printf("signalling thread2\n"); 

     pthread_cond_signal(&cond1); 

    } 

    pthread_mutex_unlock(&mutex1); 

    printf("Hey its thread 1 @ %d\n",thread1count); 
    } 
} 


void stupidfunction2(void *arg) 
{ 
int i = 0; 

    for(i = 0;i<50;i++) 
    { 

    thread2count++; 

    pthread_mutex_lock(&mutex1); 

    if((thread2count-thread1count)>5) 
    { 
       thread2waiting = 1; 

      printf("thread2 waiting \n");  

       pthread_cond_wait(&cond1,&mutex1); 

       thread2waiting = 0; 
    } 

     else if((thread1waiting == 1) && abs(thread1count-thread2count)<1) 
    { 

     printf("signalling thread1\n"); 

     pthread_cond_signal(&cond1); 
    } 

    pthread_mutex_unlock(&mutex1); 

    printf("Hey its thread 2 @ %d\n",thread2count); 
    } 
} 

OUTPUT:

Hey its thread 2 @ 1 

Hey its thread 2 @ 2 

Hey its thread 2 @ 3 

Hey its thread 2 @ 4 

Hey its thread 2 @ 5 

thread2 waiting 

Hey its thread 1 @ 1 

Hey its thread 1 @ 2 

Hey its thread 1 @ 3 

Hey its thread 1 @ 4 

Hey its thread 1 @ 5 

signalling thread2 

Hey its thread 1 @ 6 

Hey its thread 1 @ 7 

Hey its thread 1 @ 8 

Hey its thread 1 @ 9 

Hey its thread 1 @ 10 

Hey its thread 1 @ 11 

回答

0

調度程序運行時運行線程以運行它們。

方式一線程2快速運行:當線程1發信號線程2喚醒(並且在線程2實際執行之前多次),所有等待條件變量的線程必須爲調度程序運行它們。在你的例子中,我敢打賭,調度程序在線程1完成之前永遠不會進入線程2。一個很好的選擇是你的時間片是1ms。線程1發信號線程2,但是當線程2喚醒時線程1仍然有鎖定(或者因爲線程1處於稍後的循環或者因爲線程2立即起振而線程1具有互斥體在線程1之前被鎖定,稱爲cond_signal),所以線程2現在作爲互斥體上的服務器返回。

爲了例子的目的,仍然假定線程2是等待線程。

假設您希望線程2立即被喚醒...... AFAIK沒有什麼可以做的。

如果realllllly想線程1信號之後停止和你想仍然使用pthread_cond(還有其他更快的方式!),你可以信令(this allowed i think)之前沒有解鎖互斥,然後調用SCHED_YIELD()或了nanosleep( {0,0})在信號之後。性能不佳,因爲線程1只是將自己放在線程的後面(具有相同優先級)等待運行。這會增加你的上下文切換到運行時間的比率。這也是不好的,因爲你必須做額外的解鎖/鎖定。最重要的是,線程1可能在線程2之前再次喚醒!你必須使用一個循環,保持信號並屈服,直到該線程2完成它的工作! Klunky!

順便說一下,我對易失性的解釋是,它告訴編譯器該變量隨時都會改變。出於性能原因,該變量在功能開始時會複製到寄存器中變得很糟糕。編譯器很清楚知道哪些變量可以做到這一點,但不時會出錯。我會說,當優化開啓易變可能如果gcc緩存寄存器中的任何共享變量會產生影響。我不知道gcc是否在寄存器中緩存全局變量。

歡呼聲。

0

我可以在這裏看到了兩個錯誤:

  • 您還沒有鎖定周圍pthread_cond_signal會互斥。這意味着信號可以在其他線程處於檢查其是否應該繼續運行以及它進入pthread_cond_wait的點之間的時間發送,因此信號可能在此丟失。

  • 共享變量應該是易變的。編譯器可能會決定將訪問移到循環外部,或部分展開循環並省略循環體的兩個實例之間的訪問。

這些都不能直接解釋你所看到的症狀;然而,給定的行爲絕對在標準的允許範圍內。

+0

我不明白第一個錯誤 - 當第一個線程正在檢查它的條件時,另一個線程不能進入臨界區,那麼第一個線程如何錯過一個信號呢?你的行爲意味着什麼在允許的範圍內?不是cond.signal應該立即喚醒,並將其置於就緒隊列或鎖定隊列中。 – dasman 2011-01-11 09:20:50

2

直接回答你的問題:不,pthread_mutex_unlockpthread_cond_signal不立即喚醒任何等待線程。相反,他們可能會將其標記爲「準備運行」,然後操作系統會在感覺到它時安排喚醒線程。當然,操作系統可能會決定立即切換到該線程(特別是如果它比當前正在執行的線程更高的優先級),但它可能不會。

但是,您的代碼可能無法正常工作,無論如何:您可能同時運行兩個線程!

只因爲pthread_cond_wait返回,它並不意味着條件變量已被髮信號。這被稱爲「虛假喚醒」。要正確使用pthread_cond_wait,必須將其放入一個循環中,在調用pthread_cond_wait之前立即檢測與喚醒相關的條件,同時保持互斥鎖。例如

void wait_until_signalled(int* wake_flag,pthread_cond_t* cond,pthread_mutex_t* mutex) 
{ 
    pthread_mutex_lock(&mutex); 
    while(!(*wake_flag)) /* if the int pointed to by wake_flag is non-zero then wake up */ 
    { 
     pthread_cond_wait(&cond,&mutex); 
    } 
    pthread_mutex_unlock(&mutex); 
} 

void signal(int* wake_flag,pthread_cond_it* cond,pthread_mutex_t* mutex) 
{ 
    pthread_mutex_lock(&mutex); 
    *wake_flag=1; /* tell the waiting thread that it should wake */ 
    pthread_mutex_unlock(&mutex); 
    pthread_cond_signal(&cond); /* wake up the thread if it is blocked in pthread_cond_wait*/ 
} 

當然,您可能想要檢查pthread_xxx調用的返回值。

由於價值指向wake_flag僅檢查,並與鎖定互斥修改,那麼等待的線程肯定會醒,如果它被設定,直到標誌被設置爲不從wait_until_signalled返回。對pthread_cond_wait的調用自動標記線程爲等待,並解鎖互斥鎖,因此要麼調用pthread_cond_signal將看到線程正在等待,並喚醒它,以便它可以檢查標誌(已設置),或線程不是等待,這意味着它必須在signal線程設置標誌後鎖定互斥體,在這種情況下,等待線程將看到標誌設置,並返回。