C语言学习之线程同步——mutex锁、条件变量

(一)mutex锁

  1. #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

  2. int pthread_mutex_lock(pthread_mutex_t *mutex);
    功能: 如果mutex锁目前是unlocked状态,当前线程锁定这把锁,立即返回,如果mutex锁已经被其他线程锁定,当前线程挂起直到锁被解除
    参数:
    mutex 指定要操作的mutex锁
    返回值:
    成功 0
    错误 非0的错误码

  3. int pthread_mutex_trylock(pthread_mutex_t *mutex);
    功能:如果unlocked,立即上锁,否则,返回error code EBUSY
    参数:
    mutex
    返回值:
    成功 0
    错误 非0的错误码

  4. int pthread_mutex_unlock(pthread_mutex_t *mutex);
    功能:解锁 解除的锁一定是被当前线程占有的 并且被锁定的
    参数:
    mutex 指定要解除的锁
    返回值:
    成功 0
    错误 非0的错误码

  5. 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时,两个线程再公平竞争。这时,就需要用到条件变量。

  1. pthread_cond_t 条件变量类型

  2. //静态初始化一个条件变量pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

  3. int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *cond_attr);
    功能: 使用缺省属性初始化条件变量
    参数:
    cond 指定要初始化的条件变量
    cond_attr NULL 使用缺省的属性
    返回值:成功返回0,失败返回errno

  4. int pthread_cond_signal(pthread_cond_t *cond);
    功能:从等着条件变为真的线程中取出一个重新执行.如果没有线程等待条件变为真那么什么都不发生.
    参数:
    cond 指定条件变量 等着这个条件变为真
    返回值:成功返回0,失败返回errno

  5. int pthread_cond_broadcast(pthread_cond_t *cond);
    功能:重新启动所有等待条件为真的线程.如果没有线程等待条件为真,什么都不做
    参数:
    cond 指定具体的条件变量 等着这个条件变为真
    返回值:成功返回0,失败返回errno

  6. 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,执行循环之后的语句

  7. int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime);
    功能:带计时的wait

  8. 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;
}
  • 6
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值