pthread和semaphore的简单应用以及四个典型的多线程问题
pthread和semaphore的简单应用以及四个典型的多线程问题
pthread常用函数简单介绍
创建线程
int pthread_create(pthread_t * thread,
pthread_attr_t * attr,
void * (*start_routine)(void *),
void * arg)
thread是一个pthread_t类型的指针,可以简单理解为线程ID
attr表示该线程的属性,具体没有看,下面的程序中都设置成了NULL,表示默认属性。
start_routine是线程函数体的函数指针
arg是线程函数的参数
线程函数的类型是 void *fun(void *)也就是可以带一个指针参数,也可以返回一个指针。
父线程回收子线程资源
当子线程运行结束后,还有以下资源要回收。
int pthread_join(pthread_t th, void **thread_return)
th使pthread_t类型的变量,可以理解为线程ID
thread_return 是子线程函数的返回值。
初始化一个互斥锁
int pthread_mutex_init(pthread_mutex_t *mutex,
const pthread_mutex_attr_t *mutexattr);
mutex表示待初始化的互斥锁,mutexattr表示互斥锁的属性,没仔细研究,下面的程序中都是使用的NULL。表示默认属性
互斥锁枷锁和解锁
int pthread_mutex_lock(pthread_mutex *mutex);
int pthread_mutex_unlock(pthread_mutex *mutex);
销毁互斥锁
int pthread_mutex_destroy(pthread_mutex *mutex);
semaphore常用函数介绍
初始化信号量
int sem_init (sem_t *sem , int pshared, unsigned int value);
sem表示待初始化的信号量
pshared表示共享属性,Linux中貌似只能设置为0
value表示信号量的初始值
申请资源
int sem_wait(sem_t *sem);
释放资源
int sem_post(sem_t *sem);
销毁信号量
int sem_destroy(sem_t *sem);
一个常见的面试题
编写一个程序,开启3个线程,线程1输出A,线程2输出B,线程3输出C,要求输出结果必须按ABC的顺序显示;如:ABCABC….
典型的线程同步的问题:
线程1进行后线程2才能进行,然后才是线程3,线程3执行后线程1有开始执行。
也就是:
可以看到形成了一个环形,也就可能会因为出现环路等待而形成死锁,解决的办法就是,指定一个进程先执行,而且题目中让我们依次输出ABC,所以我们指定线程1先运行。
代码如下:
#include <stdio.h> #include <stdlib.h> #include <pthread.h> #include <semaphore.h> static sem_t A_B; static sem_t B_C; static sem_t C_A; void *printA(void *arg) { int i = 0; for(i = 1;i < 11;i++) { sem_wait(&C_A); printf("第%02d次:A",i); sem_post(&A_B); } return NULL; } void *printB(void *arg) { int i = 0; for(i = 1;i < 11;i++) { sem_wait(&A_B); printf("B"); sem_post(&B_C); } return NULL; } void *printC(void *arg) { int i = 0; for(i = 1;i < 11;i++) { sem_wait(&B_C); printf("C\n"); sem_post(&C_A); } return NULL; } int main() { pthread_t thread_A; pthread_t thread_B; pthread_t thread_C; sem_init(&A_B,0,0); sem_init(&B_C,0,0); sem_init(&C_A,0,1); pthread_create(&thread_A,NULL,printA,NULL); pthread_create(&thread_B,NULL,printB,NULL); pthread_create(&thread_C,NULL,printC,NULL); pthread_join(thread_A,NULL); pthread_join(thread_B,NULL); pthread_join(thread_C,NULL); sem_destroy(&A_B); sem_destroy(&B_C); sem_destroy(&C_A); printf("\n"); return 0; }
生产者消费者问题
生产者消费者之间存在的互斥和同步关系分析
首先,同一时间,只允许一个生产者对当前该生产的位置进行访问,所以生产者与生产者之间是互斥关系。
再者,同一时间,只允许一个消费者对当前该消费的位置进行消费,所以消费者与消费者之间也是互斥关系
最后,同一个位置要先生产再消费,所以生产者和消费者之间是同步关系。
我在具体是现实,用了一个指针in表示下一个待生产的位置,由于生产者和消费者线程都需要访问这个指针in,所以in是一个临界区,生产者和消费者要互斥地访问,为了变成简便我直接也把生产者和消费者看成是互斥关系,但是这也导致临界区的粒度变大。
代码如下:
#include <stdio.h> #include <pthread.h> #include <semaphore.h> #include <unistd.h> #define NUM 10 #define P_NUM 5 #define C_NUM 10 int buffer[10]; int *in; pthread_mutex_t mutex;//buffer临界区 sem_t p_sem;//消费者信号量 sem_t c_sem;//生产者信号量 void *producer(void *arg) { while(1) { sem_wait(&p_sem); pthread_mutex_lock(&mutex); printf("生产者生产了第%02d个位置的商品\n",*in); in++; pthread_mutex_unlock(&mutex); sem_post(&c_sem); sleep(2); } return NULL; } void *consumer(void *arg) { while(1) { sem_wait(&c_sem); pthread_mutex_lock(&mutex); in--; printf("消费者消耗了第%02d个位置的商品\n",*in); pthread_mutex_unlock(&mutex); sem_post(&p_sem); sleep(2); } return NULL; } int main() { int i = 1; for(i = 0;i < NUM;i++) { buffer[i] = i + 1; } in = buffer; //初始化互斥体 pthread_mutex_init(&mutex,NULL); //初始化信号量 sem_init(&p_sem,0,NUM); sem_init(&c_sem,0,0); pthread_t ppt[P_NUM]; //创建生产者线程 for(i = 0;i < P_NUM;i++) { pthread_create(&ppt[i],NULL,producer,NULL); } //创建消费者线程 pthread_t cpt[C_NUM]; for(i = 0;i < C_NUM;i++) { pthread_create(&cpt[i],NULL,consumer,NULL); } //回收资源 for(i = 0;i < P_NUM;i++) { pthread_join(ppt[i],NULL); } for(i = 0;i < C_NUM;i++) { pthread_join(cpt[i],NULL); } pthread_mutex_destroy(&mutex); sem_destroy(&p_sem); sem_destroy(&c_sem); return 0; }
读者写者问题
读者写者也是一个非常著名的同步问题。读者写者问题描述非常简单,有一个写者很多读者,多个读者可以同时读文件,但写者在写文件时不允许有读者在读文件,同样有读者在读文件时写者也不去能写文件。
读者和写者之间存在互斥关系
写者和写者之间存在互斥关系
读者和读者之间没有互斥关系,是共享关系。
这个问题的解决分为读者优先和写者优先:
读者优先是这样的:一旦有读者成功访问,那么写者将被阻塞,允许后续读者,直到没有读者后,写者才被允许访问。
写者优先是这样的:一旦后写者申请访问,那么将阻止后续读者继续访问,等当前读者读完后,写者开始写。
读者优先代码如下:
/** * 读者写者问题 * 读者优先 * 青儿哥哥 * 博客园 * 2017-09-27 * */ #include<stdio.h> #include<stdlib.h> #include<pthread.h> #define R_NUM 10 //读者个数 #define W_NUM 3 //写者个数 pthread_t rpt[R_NUM];//读者线程的id pthread_t wpt[W_NUM];//写者线程的id int readercnt = 0; int buffer = 100; pthread_mutex_t buffer_mutex;//缓冲区的临界区 pthread_mutex_t readercnt_mutex;//读者数量的临界区 void write() { int rd = rand()%100; buffer = rd; printf("写者写:%d\n",buffer); } void read() { printf("读者读:%d\n",buffer); } void *reader(void *arg) { while(1) { pthread_mutex_lock(&readercnt_mutex); readercnt++; if(readercnt == 1) { pthread_mutex_lock(&buffer_mutex); } pthread_mutex_unlock(&readercnt_mutex); read(); pthread_mutex_lock(&readercnt_mutex); readercnt--; if(readercnt == 0) { pthread_mutex_unlock(&buffer_mutex); } pthread_mutex_unlock(&readercnt_mutex); sleep(1); } return NULL; } void *writer(void *arg) { while(1) { pthread_mutex_lock(&buffer_mutex); write(); pthread_mutex_unlock(&buffer_mutex); sleep(1); } return NULL; } int main() { //初始化互斥体 pthread_mutex_init(&buffer_mutex,NULL); pthread_mutex_init(&readercnt_mutex,NULL); int i = 0; for(i = 0;i < R_NUM;i++) { pthread_create(&rpt[i],NULL,reader,NULL); } for(i = 0;i < W_NUM;i++) { pthread_create(&wpt[i],NULL,writer,NULL); } for(i = 0;i < R_NUM;i++) { pthread_join(rpt[i],NULL); } for(i = 0;i < W_NUM;i++) { pthread_join(wpt[i],NULL); } pthread_mutex_destroy(&buffer_mutex); pthread_mutex_destroy(&readercnt_mutex); return 0; }
写者优先代码如下:
/** * 读者写者问题 * 写者优先 * 青儿哥哥 * 博客园 * 2017-09-27 * */ #include<stdio.h> #include<stdlib.h> #include<pthread.h> #include<semaphore.h> #define R_NUM 10 //读者个数 #define W_NUM 3 //写者个数 pthread_t rpt[R_NUM];//读者线程的id pthread_t wpt[W_NUM];//写者线程的id int readercnt = 0; int writercnt = 0; int buffer = 100; pthread_mutex_t buffer_mutex;//缓冲区的临界区 pthread_mutex_t readercnt_mutex;//读者数量的临界区 pthread_mutex_t writercnt_mutex;//写者数量的临界区 sem_t reader_sem;//读者的信号量 void write() { int rd = rand()%100; buffer = rd; printf("写者写:%d\n",buffer); } void read() { printf("读者读:%d\n",buffer); } void *reader(void *arg) { while(1) { sem_wait(&reader_sem); sem_post(&reader_sem); pthread_mutex_lock(&readercnt_mutex); readercnt++; if(readercnt == 1) { pthread_mutex_lock(&buffer_mutex); } pthread_mutex_unlock(&readercnt_mutex); read(); pthread_mutex_lock(&readercnt_mutex); readercnt--; if(readercnt == 0) { pthread_mutex_unlock(&buffer_mutex); } pthread_mutex_unlock(&readercnt_mutex); sleep(1); } return NULL; } void *writer(void *arg) { while(1) { pthread_mutex_lock(&writercnt_mutex); writercnt++; if(writercnt == 1) { sem_wait(&reader_sem); } pthread_mutex_unlock(&writercnt_mutex); pthread_mutex_lock(&buffer_mutex); write(); pthread_mutex_unlock(&buffer_mutex); pthread_mutex_lock(&writercnt_mutex); writercnt--; if(writercnt == 0) { sem_post(&reader_sem); } pthread_mutex_unlock(&writercnt_mutex); sleep(1); } return NULL; } int main() { //初始化互斥体 pthread_mutex_init(&buffer_mutex,NULL); pthread_mutex_init(&readercnt_mutex,NULL); pthread_mutex_init(&writercnt_mutex,NULL); sem_init(&reader_sem,0,1); int i = 0; for(i = 0;i < R_NUM;i++) { pthread_create(&rpt[i],NULL,reader,NULL); } for(i = 0;i < W_NUM;i++) { pthread_create(&wpt[i],NULL,writer,NULL); } for(i = 0;i < R_NUM;i++) { pthread_join(rpt[i],NULL); } pthread_mutex_destroy(&buffer_mutex); pthread_mutex_destroy(&readercnt_mutex); pthread_mutex_destroy(&writercnt_mutex); sem_destroy(&reader_sem); return 0; }
如果你觉得对你有用,请赞一个吧~~