本文主要是介绍并发程序设计--D8D9锁和条件变量,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
临界资源概念:
不能同时访问的资源,比如写文件,只能由一个线程写,同时写会写乱。
比如外设打印机,打印的时候只能由一个程序使用。
外设基本上都是不能共享的资源。
生活中比如卫生间,同一时间只能由一个人使用。
必要性: 临界资源不可以共享
man手册找不到 pthread_mutex_xxxxxxx (提示No manual entry for pthread_mutex_xxx)的解决方法:
apt-get install manpages-posix-dev
互斥锁的创建和销毁
两种方法创建互斥锁,静态方式和动态方式
动态方式:
int pthread_mutex_init(pthread_mutex_t *restrict mutex,const pthread_mutexattr_t *restrict attr);
其中mutexattr用于指定互斥锁属性,如果为NULL则使用缺省属性。
静态方式:
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
锁的销毁:
int pthread_mutex_destroy(pthread_mutex_t *mutex)
在Linux中,互斥锁并不占用任何资源,因此LinuxThreads中的 pthread_mutex_destroy()除了检查锁状态以外(锁定状态则返回EBUSY)没有其他动作。
互斥锁的使用:
int pthread_mutex_lock(pthread_mutex_t *mutex)
int pthread_mutex_unlock(pthread_mutex_t *mutex)
int pthread_mutex_trylock(pthread_mutex_t *mutex)
vim 设置代码全文格式化:gg=G
实现两个线程同时往一个文件中写内容
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;FILE *fp;
void *func2(void *arg){pthread_detach(pthread_self());printf("This func2 thread\n");char str[]="I write func2 line\n";char c;int i=0;while(1){pthread_mutex_lock(&mutex);while(i<strlen(str)){c = str[i];fputc(c,fp);usleep(1);//休息1微秒i++;}pthread_mutex_unlock(&mutex);i=0;usleep(1);}pthread_exit("func2 exit");}void *func(void *arg){pthread_detach(pthread_self());printf("This is func1 thread\n");char str[]="You read func1 thread\n";char c;int i=0;while(1){pthread_mutex_lock(&mutex);while(i<strlen(str)){c = str[i];fputc(c,fp);i++;usleep(1);}pthread_mutex_unlock(&mutex);i=0;usleep(1);}pthread_exit("func1 exit");
}int main(){pthread_t tid,tid2;void *retv;int i;fp = fopen("1.txt","a+");if(fp==NULL){perror("fopen");return 0;}pthread_create(&tid,NULL,func,NULL);pthread_create(&tid2,NULL,func2,NULL);while(1){ sleep(1);} }
读写锁
必要性:提高线程执行效率
特性:
写者:写者使用写锁,如果当前没有读者,也没有其他写者,写者立即获得写锁;否则写者将等待,直到没有读者和写者。
读者:读者使用读锁,如果当前没有写者,读者立即获得读锁;否则读者等待,直到没有写者。
注意:
同一时刻只有一个线程可以获得写锁,同一时刻可以有多个线程获得读锁。
读写锁处于写锁状态时,所有试图对读写锁加锁的线程,不管是读者试图加读锁,还是写者试图加写锁,都会被阻塞。
读写锁处于读锁状态时,有写者试图加写锁时,之后的其他线程的读锁请求会被阻塞,以避免写者长时间的不写锁
初始化一个读写锁 pthread_rwlock_init
读锁定读写锁 pthread_rwlock_rdlock
非阻塞读锁定 pthread_rwlock_tryrdlock
写锁定读写锁 pthread_rwlock_wrlock
非阻塞写锁定 pthread_rwlock_trywrlock
解锁读写锁 pthread_rwlock_unlock
释放读写锁 pthread_rwlock_destroy
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>pthread_rwlock_t rwlock;FILE *fp;
void * read_func(void *arg){pthread_detach(pthread_self());printf("read thread\n");char buf[32]={0};while(1){//rewind(fp);pthread_rwlock_rdlock(&rwlock);while(fgets(buf,32,fp)!=NULL){printf("%d,rd=%s\n",(int)arg,buf);usleep(1000);}pthread_rwlock_unlock(&rwlock);sleep(1);}}void *func2(void *arg){pthread_detach(pthread_self());printf("This func2 thread\n");char str[]="I write func2 line\n";char c;int i=0;while(1){pthread_rwlock_wrlock(&rwlock);while(i<strlen(str)){c = str[i];fputc(c,fp);usleep(1);i++;}pthread_rwlock_unlock(&rwlock);i=0;usleep(1);}pthread_exit("func2 exit");}void *func(void *arg){pthread_detach(pthread_self());printf("This is func1 thread\n");char str[]="You read func1 thread\n";char c;int i=0;while(1){pthread_rwlock_wrlock(&rwlock);while(i<strlen(str)){c = str[i];fputc(c,fp);i++;usleep(1);}pthread_rwlock_unlock(&rwlock);i=0;usleep(1);}pthread_exit("func1 exit");
}int main(){pthread_t tid1,tid2,tid3,tid4;void *retv;int i;fp = fopen("1.txt","a+");if(fp==NULL){perror("fopen");return 0;}pthread_rwlock_init(&rwlock,NULL);pthread_create(&tid1,NULL,read_func,1);pthread_create(&tid2,NULL,read_func,2);pthread_create(&tid3,NULL,func,NULL);pthread_create(&tid4,NULL,func2,NULL);while(1){ sleep(1);} }
死锁
概念:
避免方法:
- 锁越少越好,最好使用一把锁
- 调整好锁的顺序
条件变量
应用场景:生产者消费者问题,是线程同步的一种手段。
必要性:为了实现等待某个资源,让线程休眠。提高运行效率
资源非共享、资源不是随时随地就有,需要等待
int pthread_cond_wait(pthread_cond_t *restrict cond,
pthread_mutex_t *restrict mutex);//等待资源
int pthread_cond_timedwait(pthread_cond_t *restrict cond,
pthread_mutex_t *restrict mutex,
const struct timespec *restrict abstime);//等待一定时间超时退出
int pthread_cond_signal(pthread_cond_t *cond);//放出信号,有资源了
int pthread_cond_broadcast(pthread_cond_t *cond);
使用步骤:
初始化:
静态初始化(条件变量和互斥量必须结合使用)
pthread_cond_t cond = PTHREAD_COND_INITIALIZER; //初始化条件变量
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; //初始化互斥量
或使用动态初始化
pthread_cond_init(&cond);
生产资源线程:
pthread_mutex_lock(&mutex);
开始产生资源
pthread_cond_sigal(&cond); //通知一个消费线程
或者
pthread_cond_broadcast(&cond); //广播通知多个消费线程
pthread_mutex_unlock(&mutex);
消费者线程:
pthread_mutex_lock(&mutex);
while (如果没有资源){ //防止惊群效应
pthread_cond_wait(&cond, &mutex);
}
有资源了,消费资源
pthread_mutex_unlock(&mutex);
注意:
1 pthread_cond_wait(&cond, &mutex),在没有资源等待是是先unlock 休眠,等资源到了,再lock
所以pthread_cond_wait 和 pthread_mutex_lock 必须配对使用。
2 如果pthread_cond_signal或者pthread_cond_broadcast 早于 pthread_cond_wait ,则有可能会丢失信号。
3 pthead_cond_broadcast 信号会被多个线程收到,这叫线程的惊群效应。所以需要加上判断条件while循环。
#include <pthread.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>pthread_cond_t hasTaxi=PTHREAD_COND_INITIALIZER;
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;struct taxi{struct taxi *next;int num;};struct taxi *Head=NULL;void *taxiarv(void *arg){printf("taxi arrived thread\n");pthread_detach(pthread_self());struct taxi *tx;int i=1;while(1){tx = malloc(sizeof(struct taxi));tx->num = i++;printf("taxi %d comming\n",tx->num);pthread_mutex_lock(&lock);tx->next = Head;Head = tx;pthread_cond_signal(&hasTaxi);//pthread_cond_broadcast(&hasTaxi);pthread_mutex_unlock(&lock);sleep(1);}pthread_exit(0);
}void *takeTaxi(void *arg){printf("take taxi thread\n");pthread_detach(pthread_self());struct taxi *tx;while(1){pthread_mutex_lock(&lock);while(Head==NULL){pthread_cond_wait(&hasTaxi,&lock);}tx = Head;Head=tx->next;printf("%d,Take taxi %d\n",(int)arg,tx->num);free(tx);pthread_mutex_unlock(&lock);}pthread_exit(0);
}int main(){pthread_t tid1,tid2,tid3;pthread_create(&tid1,NULL,taxiarv,NULL);
// sleep(5);pthread_create(&tid2,NULL,takeTaxi,(void*)1);pthread_create(&tid2,NULL,takeTaxi,(void*)2);pthread_create(&tid2,NULL,takeTaxi,(void*)3);while(1) {sleep(1);}}
这篇关于并发程序设计--D8D9锁和条件变量的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!