cond.signal或lock.release的问题?

时间:2011-01-11 08:17:38

标签: c multithreading locking pthreads signals

您好    当我运行以下代码时,我发现信令线程在另一个线程启动之前会继续运行很长时间......为什么会这样?一旦信号器释放锁定,它不应该被唤醒的线程吗?或者OS是否需要很长时间才能将休眠线程放回就绪队列?

#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);
    }
}

输出:

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

3 个答案:

答案 0 :(得分:2)

直接回答您的问题:不,pthread_mutex_unlockpthread_cond_signal不会立即唤醒任何等待的帖子。相反,他们可能只是将其标记为“准备好运行”,然后操作系统将在感觉就好时安排唤醒线程。当然,操作系统可能决定立即切换到该线程(特别是如果它的优先级高于当前正在执行的任何线程),但它可能不会。

但是,您的代码可能无法正常编写,无论如何:您可能同时运行两个线程!

仅仅因为pthread_cond_wait返回,并不意味着条件变量已经发出信号。这被称为“虚假唤醒”。要正确使用pthread_cond_wait,必须将其置于循环中,在调用pthread_cond_wait之前,在保持互斥锁的同时测试与唤醒相关的条件。 e.g。

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的调用将看到线程正在等待,并唤醒它以便它可以检查该标志(已经是set),或者线程没有等待,这意味着它必须在signal中的线程设置标志后锁定互斥锁,在这种情况下等待线程将看到标志设置,然后返回。

答案 1 :(得分:0)

我可以在这里看到两个错误:

  • 您没有锁定pthread_cond_signal周围的互斥锁。这意味着可以在检查其他线程是否应该继续运行以及它进入pthread_cond_wait之间的时间发送信号,因此信号可能会丢失。

  • 共享变量应该是易变的。编译器可能决定将访问移动到循环外部,或者部分展开循环并省略循环体的两个实例之间的访问。

这些都没有直接解释你所看到的症状;但是,给定的行为绝对在标准的允许范围内。

答案 2 :(得分:0)

当调度程序运行它们时,线程会运行。

方法一个线程2 doenst快速运行:当线程1发出线程2唤醒信号时(在你的情况下,线程2实际执行多次)所有等待条件变量的线程必须争取锁定时调度程序运行它们。在你的例子中,我打赌调度程序在线程1完成之前永远不会进入线程2。一个好的选择是你的时间片是1ms。

方式两个线程2没有快速运行:线程1发出线程2的信号,但是当线程2唤醒时,线程1的锁定仍然存在(因为线程1处于稍后的循环中,或者因为线程2立即醒来而线程1具有互斥锁在线程1调用cond_signal之前锁定,所以线程2现在作为服务器在互斥锁上重新入睡。

仍然假设为了示例,线程2是等待线程。

假设您希望线程2立即唤醒... AFAIK您无能为力。

如果您确实希望线程1在发送信号后停止并且您仍想使用pthread_cond(还有其他更快的方法!),您可以在发信号(this allowed i think)之前解锁互斥锁,然后调用sched_yield()或信号后的nanosleep({0,0})。性能不好,因为线程1只是将自己置于线程(具有相同优先级)的后面等待运行。这将增加上下文切换与运行时的比率。这也很糟糕,因为你必须做额外的解锁/锁定。除此之外,线程1可能会在线程2之前再次唤醒!你必须使用循环,保持信号和屈服,直到darn线程2完成它的工作! Klunky!

顺便说一句,我对volatile的解释是它告诉编译器该变量随时都会发生变化。出于性能原因,该变量在函数开始时复制到寄存器中是不好的。编译器非常了解哪些变量可以做到这一点,但有时会出错。我会说当启用优化时,如果gcc缓存了寄存器中的任何共享变量,那么可能会产生影响。我不知道gcc是否在寄存器中缓存全局变量。

欢呼声。