#include
#include
#include
#include
#include
#include
#define unsigned int unit;
int g_Flag = 0;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
void printids(const char *s);
void *thread1(void *);
void *thread2(void *);
void *thread3(void *);
void lock(const char *s);
void unlock(const char *s);
int main(int argc, char **argv)
{
printf("----------main_funtion_begin-----------\n");
printf("Enter main function.\n");
pthread_t tid1, tid2, tid3;
int err1 = 0, err2 = 0, err3 = 0;
void *rval1, *rval2, *rval3;
err1 = pthread_create(&tid1, NULL, thread1, NULL);
if(err1 != 0)
printf("%s: %d\n", __func__, strerror(err1));
printf("------tid1_created\n");
err2 = pthread_create(&tid2, NULL, thread2, NULL);
if(err2 != 0)
printf("%s: %d\n", __func__, strerror(err2));
printf("------tid2_created\n");
err3 = pthread_create(&tid3, NULL, thread3, NULL);
if(err3 != 0)
printf("%s: %d\n", __func__, strerror(err3));
printf("------tid3_created\n");
printf("------tid1_will_join======\n");
err1 = pthread_join(tid1, &rval1);
printf("======tid1_joined");
if(err1 != 0)
printf("%s: %d\n", __func__, strerror(err1));
printf("thread1 exit code is %d.\n", (int)rval1);
err3 = pthread_join(tid3, &rval3);
printf("======tid3_joined");
if(err3 != 0)
printf("%s: %d\n", __func__, strerror(err3));
printf("thread3 exit code is %d.\n", (int)rval3);
err2 = pthread_join(tid2, &rval2);
printf("======tid2_joined");
if(err2 != 0)
printf("%s: %d\n", __func__, strerror(err2));
printf("thread2 exit code is %d.\n", (int)rval2);
printids("This is the main thread");
printf("Leave main function.\n");
printf("----------main_function_end----------\n");
exit(0);
}
void lock(const char *s)
{
if (pthread_mutex_lock(&mutex) != 0)
{
printf("%s has lock error", s);
exit(0);
}
}
void unlock(const char *s)
{
if (pthread_mutex_unlock(&mutex) != 0)
{
printf("%s has unlock error", s);
exit(0);
}
}
void printids(const char *s)
{
pid_t pid;
pthread_t tid;
pid = getpid();
tid = pthread_self();
printf("%s, process ID is %u, thread ID is %u, and g_Flag is %d.\n", s, (uint)pid, (uint)tid, g_Flag);
}
void *thread1(void *arg)
{
lock("thread1");
printf("Enter thread1.\n");
printids("This is thread1");
int i;
for( i = 0; i < 1000; i++)
g_Flag++;
printids("This is thread1");
printf("Leave thread1.\n");
unlock("thread1");
pthread_exit(0);
}
void *thread2(void *arg)
{
lock("thread2");
printf("Enter thread2.\n");
printids("This is thread2");
int i;
for( i = 0; i < 1000; i++)
g_Flag++;
printids("This is thread2");
printf("Leave thread2.\n");
unlock("thread2");
return (void *)2;
}
void *thread3(void *arg)
{
lock("thread3");
printf("Enter thread3.\n");
printids("This is thread3");
int i;
for( i = 0; i < 1000; i++)
g_Flag++;
printids("This is thread3");
printf("Leave thread3.\n");
unlock("thread3");
return (void *)3;
}
This code can use to test the sequence of light weight process.
Run the code with the following commmand:
$ gcc filename -o newname -lpthread -fpermissive
$ ./newname
use g++ to run C++ code.
pthread_create()
pthread_join()
pthread_mutex_lock()
pthread_mutex_unlock()
pthread_cond_timedwait(cond, mutex, abstime);
pthread_cond_wait(cond, mutex);
pthread_cond_signal(cond); 将至少解锁一个线程(阻塞在条件变量上的线程)。
pthread_cond_broadcast(cond) : 将对所有阻塞在条件变量上的线程解锁。
线程1调用pthread_cond_wait() 所做的事 三个部分:
同时对mutex解锁,
并等待条件 cond 发生
获得通知后,对mutex加锁;
调用pthread_cond_wait()后,同时对mutex解锁,并等待条件 cond 发生(要求解锁并阻塞是一个原子操作)
现在互斥对象已被解锁,其它线程可以进入互斥区域,修改条件。
此时,pthread_cond_wait() 调用还未返回。等待条件 mycond是一个阻塞操作,这意味着线程将睡眠,在它苏醒之前不会消耗 CPU 周期。直到特定条件发生。
假设另一个线程2对mutex加锁, 并改变条件, 然后调用函数 pthread_cond_signal() 激活等待条件。这意味着线程1现在将苏醒。此时线程1试图对mutex加锁,由于线程2还没有对mutex解锁,所以线程1只有等待,只有在线程2对mutex解锁后,线程1优先获得mutex加锁,然后就能做想做的事情了。
在多核处理器下,pthread_cond_signal可能会激活多于一个线程(阻塞在条件变量上的线程)。
On a multi-processor, it may be impossible for an implementation of pthread_cond_signal() to avoid the unblocking of more than one thread blocked on a condition variable.
结果是,当一个线程调用pthread_cond_signal()后,多个调用pthread_cond_wait()或pthread_cond_timedwait()的线程返回。这种效应成为”虚假唤醒”(spurious wakeup)
The effect is that more than one thread can return from its call to pthread_cond_wait() or pthread_cond_timedwait() as a result of one call to pthread_cond_signal(). This effect is called “spurious wakeup”. Note that the situation is self-correcting in that the number of threads that are so awakened is finite; for example, the next thread to call pthread_cond_wait() after the sequence of events above blocks.
虽然虚假唤醒在pthread_cond_wait函数中可以解决,为了发生概率很低的情况而降低边缘条件(fringe condition)效率是不值得的,纠正这个问题会降低对所有基于它的所有更高级的同步操作的并发度。所以pthread_cond_wait的实现上没有去解决它。
While this problem could be resolved, the loss of efficiency for a fringe condition that occurs only rarely is unacceptable, especially given that one has to check the predicate associated with a condition variable anyway. Correcting this problem would unnecessarily reduce the degree of concurrency in this basic building block for all higher-level synchronization operations.
所以通常的标准解决办法是这样的:
pthread_cond_wait中的while()不仅仅在等待条件变量前检查条件变量,实际上在等待条件变量后也检查条件变量。
这样对condition进行多做一次判断,即可避免“虚假唤醒”.
这就是为什么在pthread_cond_wait()前要加一个while循环来判断条件是否为假的原因。
添加while检查的做法被认为是增加了程序的健壮性,在IEEE Std 1003.1-2001中认为spurious wakeup是允许的。
An added benefit of allowing spurious wakeups is that applications are forced to code a predicate-testing-loop around the condition wait. This also makes the application tolerate superfluous condition broadcasts or signals on the same condition variable that may be coded in some other part of the application. The resulting applications are thus more robust. Therefore, IEEE Std 1003.1-2001 explicitly documents that spurious wakeups may occur.
以下是条件变量的示例代码:
#include
#include
#include
#include
#include
#include
#define unsigned int unit;
int g_Flag = 0;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
void printids(const char *s);
void lock(const char *s);
void unlock(const char *s);
void *thread1(void *);
void *thread2(void *);
int main(int argc, char **argv)
{
printf("Enter main function.\n");
pthread_t tid1, tid2;
int err1 = 0, err2 = 0;
void *rval1, *rval2;
err1 = pthread_create(&tid1, NULL, thread1, NULL);
if(err1 != 0)
printf("%s: %d\n", __func__, strerror(err1));
err2 = pthread_create(&tid2, NULL, thread2, NULL);
if(err2 != 0)
printf("%s: %d\n", __func__, strerror(err2));
//err1 = pthread_join(tid1, &rval1);
//if(err1 != 0)
// printf("%s: %d\n", __func__, strerror(err1));
//printf("thread1 exit code is %d.\n", (int)rval1);
//err2 = pthread_join(tid2, &rval2);
//if(err2 != 0)
// printf("%s: %d\n", __func__, strerror(err2));
//printf("thread2 exit code is %d.\n", (int)rval2);
pthread_cond_wait(&cond, &mutex);
printids("This is the main thread");
printf("Leave main function.\n");
exit(0);
}
void printids(const char *s)
{
pid_t pid;
pthread_t tid;
pid = getpid();
tid = pthread_self();
printf("%s, process ID is %u, thread ID is %u, and g_Flag is %d.\n", s, (uint)pid, (uint)tid, g_Flag);
}
void lock(const char *s)
{
if(pthread_mutex_lock(&mutex) != 0)
{
printf("%s has lock error!", s);
exit(0);
}
}
void unlock(const char *s)
{
if(pthread_mutex_unlock(&mutex) != 0)
{
printf("%s has unlock error!", s);
exit(0);
}
}
void *thread1(void *arg)
{
lock("Thread1");
printf("Enter thread1.\n");
printids("This is thread1");
if(g_Flag == 2)
pthread_cond_signal(&cond);
g_Flag = 1;
printids("This is thread1");
printf("Leave thread1.\n");
unlock("Thread1");
pthread_exit(0);
}
void *thread2(void *arg)
{
lock("Thread2");
printf("Enter thread2.\n");
printids("This is thread2");
if(g_Flag == 1)
pthread_cond_signal(&cond);
g_Flag = 2;
printids("This is thread2");
printf("Leave thread2.\n");
unlock("Thread2");
return (void *)2;
}
[1]. linux c 线程间同步(通信)的几种方法–互斥锁,条件变量,信号量,读写锁
[2]. linux基础——linux线程间通信及同步机制总结
[3]. Linux信号量详解