2011-01-11 56 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线程设置标志后锁定互斥体,在这种情况下,等待线程将看到标志设置,并返回。