(一)mutex锁
-
#include <pthread.h>
//静态初始化一个mutex类型的变量
pthread_mutex_t fastmutex = PTHREAD_MUTEX_INITIALIZER;
int pthread_mutex_init(pthread_mutex_t *mutex,
const pthread_mutexattr_t *mutexattr);
功能: 使用缺省的属性初始化mutex锁.
参数:
mutex 指定要初始化的mutex锁
mutexattr NULL 使用缺省的属性初始化mutex锁
返回值:
总是返回0 -
int pthread_mutex_lock(pthread_mutex_t *mutex);
功能: 如果mutex锁目前是unlocked状态,当前线程锁定这把锁,立即返回,如果mutex锁已经被其他线程锁定,当前线程挂起直到锁被解除
参数:
mutex 指定要操作的mutex锁
返回值:
成功 0
错误 非0的错误码 -
int pthread_mutex_trylock(pthread_mutex_t *mutex);
功能:如果unlocked,立即上锁,否则,返回error code EBUSY
参数:
mutex
返回值:
成功 0
错误 非0的错误码 -
int pthread_mutex_unlock(pthread_mutex_t *mutex);
功能:解锁 解除的锁一定是被当前线程占有的 并且被锁定的
参数:
mutex 指定要解除的锁
返回值:
成功 0
错误 非0的错误码 -
int pthread_mutex_destroy(pthread_mutex_t *mutex);
功能: 销毁mutex锁 释放资源
参数:
mutex 指定要被销毁的mutex锁
返回值:
成功 0
错误 非0的错误码
mutex.c
#include <pthread.h> #include <stdio.h> int v = 0; //静态初始化mutex锁,也可以调用pthread_mutex_init() pthread_mutex_t mut = PTHREAD_MUTEX_INITIALIZER; void *handle(void *arg){ int tmp; for(int i=0; i<1000; i++){ pthread_mutex_lock(&mut);//在访问公共资源之前加锁 tmp = v; tmp++; printf("tmp=%d...%lu\n", tmp, pthread_self()); v = tmp; pthread_mutex_unlock(&mut);//公共资源访问结束后解锁 } return NULL; } int main(void){ //创建两个线程 pthread_t tid, cid; pthread_create(&tid, NULL, handle, NULL); pthread_create(&cid, NULL, handle, NULL); //阻塞等待线程的汇合 pthread_join(tid, NULL); pthread_join(cid, NULL); //销毁mut锁对象 pthread_mutex_destroy(&mut); return 0; }
执行结果:
执行分析:如果不加mutex锁的情况下,开两个线程执行handle方法,由于两个线程共享全局变量v,所以tmp不会打到2000,加了锁之后,同一时刻只有一个线程可以访问全局变量v,所以tmp最终会到2000.
(二)条件变量
锁只能保证同一时刻只有一个线程访问公共资源,但并不能很好的协调两个线程工作。比如,如果实现生产者消费者模型,将v作为公共资源,生产者线程使v增加,消费者线程使v减少,当v为0时消费者不能消费。在实现这个模型时,如果只是用mutex锁实现,相当于两个线程无论在什么情况下,都会是公平的竞争公共资源,但有时我们并不希望这样。比如,当v为0时,我们不希望消费者线程再占用CPU资源,直接让生产者控制全局变量v就好,但是mutex会公平地让两个线程竞争。如果消费者线程竞争胜利,那么由于v=0,消费者只是if(v=0) do nothing 的语句执行一下,白白占用CPU,而实际情况我们更希望在这种情况下,直接让消费者让出cpu,直接让生产者控制,直至v>0时,两个线程再公平竞争。这时,就需要用到条件变量。
-
pthread_cond_t 条件变量类型
-
//静态初始化一个条件变量pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
-
int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *cond_attr);
功能: 使用缺省属性初始化条件变量
参数:
cond 指定要初始化的条件变量
cond_attr NULL 使用缺省的属性
返回值:成功返回0,失败返回errno -
int pthread_cond_signal(pthread_cond_t *cond);
功能:从等着条件变为真的线程中取出一个重新执行.如果没有线程等待条件变为真那么什么都不发生.
参数:
cond 指定条件变量 等着这个条件变为真
返回值:成功返回0,失败返回errno -
int pthread_cond_broadcast(pthread_cond_t *cond);
功能:重新启动所有等待条件为真的线程.如果没有线程等待条件为真,什么都不做
参数:
cond 指定具体的条件变量 等着这个条件变为真
返回值:成功返回0,失败返回errno -
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
功能:解锁(使用这个函数之前,一定加锁),挂起等待cond is signaled,直到cond is signaled该函数返回,返回之前重新获得锁(重新上锁)
参数:
cond 指定要操作的条件变量
mutex 使用到的mutex锁
返回值:成功返回0,失败返回errno
该函数通常配合while语句使用,当while条件满足时,则调用该函数阻塞,当其他线程完成某项工作,使while条件满足了,就调用signal函数,wait函数返回并重新获得锁,回到while循环,由于此时条件不满足,则不在进入循环调用wait,执行循环之后的语句 -
int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime);
功能:带计时的wait -
int pthread_cond_destroy(pthread_cond_t *cond);
功能:销毁条件变量
参数:
cond 指定要销毁的条件变量
返回值:
成功 0
错误 非0
用条件变量实现上述生产者消费者模型(不对生产者限制,只限制消费者,只有有货物时才可以消费)
cond.c
#include <t_stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
//定义节点类型
typedef struct node{
int data;
struct node *next;
}node_t;
node_t *head = NULL;//定义一个空链表
pthread_mutex_t mutex;//定义一个锁
pthread_cond_t cond;//定义一个条件变量
//生产者线程
void *p_func(void *arg){
node_t *new = NULL;
while(1){
//生产一个新的节点
new = (node_t *)calloc(1, sizeof(node_t));
new->data = rand() % 1000 + 1;
new->next = NULL;
printf("p:%d\n", new->data);
//加锁
pthread_mutex_lock(&mutex);
//将新节点插入到链表的头部
new->next = head;
head = new;
//解锁
pthread_mutex_unlock(&mutex);
//通知等待条件变量变为真的消费者
pthread_cond_signal(&cond);
//休眠1~4秒
sleep(rand()%4+1);
}
return NULL;
}
//消费者线程
void *c_func(void *arg){
node_t *tmp = NULL;
while(1){
//加锁
pthread_mutex_lock(&mutex);
while(!head){
//阻塞等待生产者生产
pthread_cond_wait(&cond, &mutex);
}
//从链表的头部摘取一个节点
tmp = head;
head = head->next;
//解锁
pthread_mutex_unlock(&mutex);
//消费摘取的节点
printf("c:%d\n", tmp->data);
free(tmp);
tmp = NULL;
//休眠1~4秒
sleep(rand()%4+1);
}
return 0;
}
int main(void){
srand(time(NULL));
//初始化锁,初始化条件变量
pthread_mutex_init(&mutex, NULL);
pthread_cond_init(&cond, NULL);
//创建两个线程,一个用于生产者,一个用于消费者
pthread_t tid, cid;
pthread_create(&tid, NULL, p_func, NULL);
pthread_create(&cid, NULL, c_func, NULL);
//阻塞等待线程汇合
pthread_join(tid, NULL);
pthread_join(cid, NULL);
//销毁锁,销毁条件变量
pthread_mutex_destroy(&mutex);
pthread_cond_destroy(&cond);
return 0;
}
用信号和自定义信号处理函数及pause函数模拟用条件变量实现的生产者消费者模型:只是模拟,方便深入理解条件变量的过程,很多细节肯定是条件变量更优。int v作为公共变量,生产者使它增加,消费者使它减少.
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>
#include <time.h>
#include <signal.h>
#include <sys/types.h>
void my_block(int n){
printf("unblock\n");
return;
}
int v = 0;
//静态初始化mutex锁,也可以调用pthread_mutex_init()
pthread_mutex_t mut = PTHREAD_MUTEX_INITIALIZER;
void *producer(void *arg){
while(1){
pthread_mutex_lock(&mut);
v++;
printf("%d:+1\n", v);
kill(getpid(), 14);
pthread_mutex_unlock(&mut);
sleep(rand()%3+1);
}
}
void *consumer(void *arg){
while(1){
pthread_mutex_lock(&mut);
if(v>0){
v--;
printf("%d:-1\n", v);
}else{
pthread_mutex_unlock(&mut);
pause();//能阻塞线程吗?专门阻塞线程
continue;
}
pthread_mutex_unlock(&mut);
sleep(rand()%3+1);
}
}
int main(void){
signal(14, my_block);
srand(time(NULL));
//创建两个线程
pthread_t tid, cid;
pthread_create(&tid, NULL, producer, NULL);
pthread_create(&cid, NULL, consumer, NULL);
//阻塞等待线程的汇合
pthread_join(tid, NULL);
pthread_join(cid, NULL);
//销毁mut锁对象
pthread_mutex_destroy(&mut);
return 0;
}