您好 当我运行以下代码时,我发现信令线程在另一个线程启动之前会继续运行很长时间......为什么会这样?一旦信号器释放锁定,它不应该被唤醒的线程吗?或者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
答案 0 :(得分:2)
直接回答您的问题:不,pthread_mutex_unlock
和pthread_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是否在寄存器中缓存全局变量。
欢呼声。