条件变量可以说是互斥锁的补充,但不同的是条件变量是用来等待而不是上锁的,当收到条件变量时线程被唤醒,执行到等待函数(pthread_cond_wait)时又阻塞等待条件变量成立。与信号量不同的是,信号量类似于计数器,需要在程序中进行加一减一操作,条件变量有自动置位功能(通过下面实验能体会到);而且条件变量能用一个信号唤醒多个线程。
条件变量使用步骤:
1.分配条件变量,静态分配将PTHREAD_COND_INITIALIZER赋值给条件变量,动态分配使用pthread_cond_init;
2.通知或等待条件变量;
3.销毁动态分配的条件变量。
常用函数:
pthread_cond_init()
:动态分配条件变量,
#include <pthread.h>
int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr);
参数含义:
cond:要初始化的目标条件变量
attr:初始化属性,设置为 NULL表示使用默认属性
返回值:成功返回0,出错返回错误码。
pthread_cond_signal()
:唤醒一条等待线程
int pthread_cond_signal(pthread_cond_t *cond);
pthread_cond_broadcast()
:唤醒所有等待线程,若想唤醒多个线程条件变量只能和一个互斥锁绑定(参考实验)
int pthread_cond_broadcast(pthread_cond_t *cond);
参数含义:cond,目标条件变量,唤醒等待此条件变量的线程。
返回值:成功返回0,出错返回错误码。
pthread_cond_wait()
:阻塞此线程
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t * mutex);
参数含义:
cond:等待的条件变量,
mutex:互斥锁。
返回值:成功返回0,出错返回错误码。
实验1代码在sync/cond_1目录下:路径为:11_Linux系统开发进阶\Linux系统编程_章节使用资料。
yellow线程每过三秒发一次信号,同时唤醒其他线程(red,green,blue),使用一个条件变量唤醒多个线程的必要条件是一个条件变量只能和一个互斥锁绑定。
mani.c:创建线程四个线程,定义一个条件变量,一个互斥锁。
#include <unistd.h>
#include <pthread.h>
#include "thread.h"
pthread_mutex_t Mutex =PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t Cond =PTHREAD_COND_INITIALIZER;
int main(int argc, const char *argv[])
{
pthread_t yellowId,redId,greenId,blueId;
//创建线程
pthread_create(&yellowId,NULL,&yellowFunc,"Yellow");
pthread_create(&redId,NULL,&redFunc,"Red");
pthread_create(&greenId,NULL,&greenFunc,"Green");
pthread_create(&blueId,NULL,&blueFunc,"Blue");
pthread_detach(yellowId);
pthread_detach(redId);
pthread_detach(greenId);
pthread_detach(blueId);
while(1)
sleep(2);
return 0;
}
thread.h:
#ifndef __COND__H__
#define __COND__H__
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
extern pthread_mutex_t Mutex ;
extern pthread_cond_t Cond;
int i;
void * yellowFunc(void * arg);
void * redFunc(void * arg);
void * greenFunc(void * arg);
void * blueFunc(void * arg);
#endif
thread.c:实现线程打印功能,使用互斥锁,条件变量同步。
#include <string.h>
#include "thread.h"
void errExit(int r)
{
if(r != 0)
{
printf("\033[1;33m %s . \033[0m\n",strerror(r));
pthread_exit(NULL);
}
}
void * yellowFunc(void * arg)
{
int b;
while(1){
b = pthread_mutex_lock(&Mutex);//上锁
errExit(b);
printf("\033[1;33m yellow 线程, time=%ds \033[0m\n",i);
i++;
b = pthread_mutex_unlock(&Mutex);//解锁
errExit(b);
sleep(1);
if(i == 3){//每3秒发一次信号,唤醒其他线程
b = pthread_cond_broadcast(&Cond);//发出信号,唤醒等待此条件变量的线程
errExit(b);
}
}
}
void * redFunc(void * arg)
{
int b;
while(1){
b = pthread_mutex_lock(&Mutex);//互斥锁
errExit(b);
b = pthread_cond_wait(&Cond,&Mutex);//等待Cond条件变量唤醒
errExit(b);
i=0;
printf("\033[1;31m red 线程, time=%ds \033[0m\n",i);
b = pthread_mutex_unlock(&Mutex);
errExit(b);
}
}
void * greenFunc(void * arg)
{
int b;
while(1){
b = pthread_mutex_lock(&Mutex);
errExit(b);
b = pthread_cond_wait(&Cond,&Mutex);//等待Cond条件变量
errExit(b);
i=0;
printf("\033[1;32m green 线程, time=%ds \033[0m\n",i);
b = pthread_mutex_unlock(&Mutex);
errExit(b);
}
}
void * blueFunc(void * arg)
{
int b;
while(1){
b = pthread_mutex_lock(&Mutex);
errExit(b);
b = pthread_cond_wait(&Cond,&Mutex);//等待Cond条件变量
errExit(b);
i=0;
printf("\033[1;34m blue 线程, time=%ds \033[0m\n",i);
b = pthread_mutex_unlock(&Mutex);
errExit(b);
}
}
具体代码在sync/cond/thread.c,执行 make编译运行,程序每过三秒同时唤醒其他线程,可看到打印信息:
实验2代码在cond_2目录下:路径为:11_Linux系统开发进阶\Linux系统编程_章节使用资料。
程序中使用条件变量依次执行各线程,
main.c:创建线程,静态初始化互斥锁和条件变量,
#include <unistd.h>
#include <pthread.h>
#include "thread.h"
pthread_mutex_t yellowMutex =PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t redMutex =PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t greenMutex =PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t blueMutex =PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t yellowCond =PTHREAD_COND_INITIALIZER;
pthread_cond_t redCond =PTHREAD_COND_INITIALIZER;
pthread_cond_t greenCond =PTHREAD_COND_INITIALIZER;
pthread_cond_t blueCond =PTHREAD_COND_INITIALIZER;
int main(int argc, const char *argv[])
{
pthread_t yellowId,redId,greenId,blueId;
//创建线程
pthread_create(&yellowId,NULL,&yellowFunc,"Yellow");
pthread_create(&redId,NULL,&redFunc,"Red");
pthread_create(&greenId,NULL,&greenFunc,"Green");
pthread_create(&blueId,NULL,&blueFunc,"Blue");
pthread_cond_signal(&blueCond);
pthread_detach(yellowId);
pthread_detach(redId);
pthread_detach(greenId);
pthread_detach(blueId);
while(1)
sleep(2);
return 0;
}
thread.h:
#ifndef __COND__H__
#define __COND__H__
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
extern pthread_mutex_t yellowMutex;
extern pthread_mutex_t redMutex;
extern pthread_mutex_t greenMutex;
extern pthread_mutex_t blueMutex;
extern pthread_cond_t yellowCond;
extern pthread_cond_t redCond;
extern pthread_cond_t greenCond;
extern pthread_cond_t blueCond;
int i;
void * yellowFunc(void * arg);
void * redFunc(void * arg);
void * greenFunc(void * arg);
void * blueFunc(void * arg);
#endif
thread.c:依次唤醒线程,main.c中首先发blueCond唤醒yellow线程,yellow唤醒red,red唤醒green,green唤醒blue,blue唤醒yellow,就会一直循环下去,
#include <string.h>
#include "thread.h"
void * yellowFunc(void * arg)
{
int b;
while(1){
pthread_mutex_lock(&yellowMutex);//上锁
pthread_cond_wait(&blueCond,&yellowMutex);//等待blue条件变量唤醒
printf("\033[1;33m yellow 线程, time=%ds \033[0m\n",i);
i++;
sleep(1);
pthread_cond_signal(&yellowCond);//发出yello信号,唤醒red线程
pthread_mutex_unlock(&yellowMutex);//解锁
}
}
void * redFunc(void * arg)
{
int b;
while(1){
pthread_mutex_lock(&redMutex);//互斥锁
pthread_cond_wait(&yellowCond,&redMutex);//等待yellow条件变量唤醒
printf("\033[1;31m red 线程, time=%ds \033[0m\n",i);
i++;
sleep(1);
pthread_cond_signal(&redCond);//发出red信号,唤醒green线程
pthread_mutex_unlock(&redMutex);
}
}
void * greenFunc(void * arg)
{
int b;
while(1){
pthread_mutex_lock(&greenMutex);
pthread_cond_wait(&redCond,&greenMutex);//等待red条件变量
printf("\033[1;32m green 线程, time=%ds \033[0m\n",i);
i++;
sleep(1);
pthread_cond_signal(&greenCond);//发出green信号,唤醒blue线程
pthread_mutex_unlock(&greenMutex);
}
}
void * blueFunc(void * arg)
{
int b;
while(1){
pthread_mutex_lock(&blueMutex);
pthread_cond_wait(&greenCond,&blueMutex);//等待green条件变量
printf("\033[1;34m blue 线程, time=%ds \033[0m\n",i);
i++;
sleep(1);
pthread_cond_signal(&blueCond);//发出blue信号,唤醒yellow线程
pthread_mutex_unlock(&blueMutex);
}
}
编译运行:
能看到线程按条件变量设置的顺序依次启动。