线程/进程同步:解决线程/进程之间的竞争关系或者协作关系(线程中竞争关系会更多,因为线程数据共享会很多,会访问共享数据,访问共享数据会出现竞争)
线程同步方法:互斥量(互斥锁)、信号量---线程库中的信号量、条件变量、读写锁
一、互斥锁:
- 操作的实体是锁,锁的状态有两种:加锁和解锁
- 线程在进入临界区之前,加锁操作;如果锁是加锁状态,则执行加锁操作将被阻塞
- 临界区就是共享资源的访问代码
- 线程退出临界区之后,解锁操作
- 互斥锁就是因为这把锁只允许一个线程进行加锁
1、互斥锁类型:
prhread_mutex_t;一般将锁定义到全局
2、 互斥锁操作:
(1)初始化
int pthread_mutex_init(prhread_mutex_t *mutex,pthread_mutexattr_t *attr);
- prhread_mutex_t *mutex:锁类型
- pthread_mutexattr_t *attr:锁的属性;默认属性为NULL
(2)加锁
int pthread_mutex_lock(prhread_mutex_t *mutex);//如果锁是加锁状态,则执行加锁操作将被阻塞
int pthread_mutex_trylock(prhread_mutex_t *mutex);//如果锁是加锁状态,则执行加锁操作不会被阻塞
(3)解锁
int pthread_mutex_unlock(prhread_mutex_t *mutex);
(4)销毁
int pthread_mutex_destroy(prhread_mutex_t *mutex);
3、例题:主线程负责接收用户输入,函数线程负责将用户输入打印到终端界面(最好用信号量实现,因为互斥锁更多的用在竞争关系)
- 如何将数据通过主线程传递到函数线程?通过全局空间。
- 如何让主线程和函数线程同步?
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <assert.h>
#include <pthread.h>
#include <time.h>pthread_mutex_t mutex;//定义一个锁char buff[128]={0};//用来数据传递void *fun(void *arg)
{while(1){pthread_mutex_lock(&mutex);if(strncmp(buff,"end",3)==0){break;}printf("fun:%s\n",buff);memset(buff,0,128);int n=rand() %3+1;sleep(n);pthread_mutex_unlock(&mutex);n=rand() %3+1;sleep(n);}
}int main()
{stand((unsigned int)(time(NULL)*time(NULL)));pthread_mutex_init(&mutex,NULL);//初始化的锁是解锁状态pthread_t id;int res=pthread_create(&id,NULL,fun,NULL);assert(res==0);while(1){pthread_mutex_lock(&mutex);//加锁printf("input:");fgets(buff,127,stdin);pthread_mutex_unlock(&mutex);//解锁if(strncmp(buff,"end",3)==0) {break;}n=rand() %3+1;sleep(n);} pthread_join(id,NULL);pthread_mutex_destroy(&mutex);exit(0);
}
二、信号量:特殊的一个计数器(进程的信号量可以在线程使用,但是线程的信号量不能在进程中使用)
1、信号量类型:全局,sem_t
2、初始化:
int sem_init(sem_t *sem, int shared, int val);
- sem_t *sem:sem_t类型的信号量的变量
- shared:设定是否可以在进程之间共用,Linux目前不支持,一般为0,代表进程间不共享
- val:初始值
3、P操作:
int sem_wait(sem_t *sem);
- 如果信号量的值为0,P操作会阻塞
4、V操作
int sem_post(sem_t *sem);
5、销毁:
int sem_destroy(sem_t *sem);
6、例题:主线程负责接收用户输入,函数线程负责将用户输入打印到终端界面(最好用信号量实现,因为互斥锁更多的用在竞争关系)
函数线程对主线程信号量为1,主线程对函数线程为0
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <assert.h>
#include <pthread.h>
#include <semaphore.h>sem_t sem1;
sem_t sem2;//定义两个信号量char buff[128]={0};void *fun(void *arg)
{while(1){sem_wait(&sem1);//p操作if(strncmp(buff,"end",3)==0){break;}printf("fun: %s",buff)memset(buff,0,128);sem_pos(&sem2);//函数线程唤醒主线程}
}int main()
{sem_init(&sem1,0,0);sem_init(&sem2,0,1);int res=pthread_create(&id,NULL,fun,NULL);assert(res==0);while(1){sem_wait(&sem2);printf("input:");fgets(buff,127,stdin);sem_pos(&sem1);//主线程唤醒函数线程if(strncmp(buff,"end",3)==0){break;}}pthread_join(id,NULL);sem_destroy(&sem1);sem_destroy(&sem2);return 0;
}
三、条件变量:提供了一种线程间的通知机制,当某个共享数据达到某个值时,唤醒等待这个共享数据的线程
1、初始化
int pthread_cond_init(pthread_cond_t*cond, pthread_condattr_t * attr);
attr表条件变量属性,通常为默认值,传NULL即可
变量cond只有两种取值1、0
2、阻塞等待条件变量发生
int pthread_cond_wait(pthread_cond_tcond,pthread_mutex_tmutex);
用法:
1、 pthread_mutex_lock(&mutex);
2、 pthread_cond_wait(&cond,&mutex);
(1)mutex加锁
(2)wait调用,将线程放到cond现成列表中,然后解锁
(3)wait返回时,mutex又会被锁住(条件发生的时候返回)
(4)对mutex解锁3、pthread_mutex_unlock(&mutex);
3、唤醒单个线程:
int pthread_cond_signal(pthread_cond_t*cond);////前面需要进行一次lock,后面进行一次unlock
4、唤醒所有等待的线程:
int pthread_cond_broadcast(pthread_cond_t * cond);//前面需要进行一次lock,后面进行一次unlock
5、销毁
int pthread_cond_destroy(pthread_cont_t * cond);
6、代码实现
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>#include <pthread.h>
char buff[128] = {0};pthread_cond_t cond; // 条件变量
pthread_mutex_t mutex; // 互斥锁void *fun(void *arg)
{char *s = (char*)arg;while(1){pthread_mutex_lock(&mutex);pthread_cond_wait(&cond, &mutex); // wait是要阻塞的,直到有线程对cond进行signal或者broadcastpthread_mutex_unlock(&mutex);if(strncmp(buff, "end", 3) == 0){break;}printf("%s: %s", s, buff);}
}int main()
{pthread_mutex_init(&mutex, NULL);pthread_cond_init(&cond, NULL);pthread_t id[2];int res = pthread_create(&id[0], NULL, fun, "thread1");assert(res == 0);res = pthread_create(&id[1], NULL, fun, "thread2");assert(res == 0);while(1){printf("input: ");fgets(buff, 127, stdin);if(strncmp(buff, "end", 3) == 0) // 唤醒在条件变量cond上等待的所有线程{pthread_mutex_lock(&mutex);pthread_cond_broadcast(&cond);pthread_mutex_unlock(&mutex);break;}else // 唤醒在条件变量cond上等待的一个线程{pthread_mutex_lock(&mutex);pthread_cond_signal(&cond);pthread_mutex_unlock(&mutex);}}pthread_join(id[0], NULL);pthread_join(id[1], NULL);pthread_mutex_destroy(&mutex);pthread_cond_destroy(&cond);exit(0);
}
四、读写锁
1、概念:读写锁与互斥量类似,不过读写锁允许更高的并行性。读写锁可以有三种状态:读模式下加锁状态,写模式下加锁状态,不加锁状态。
2、初始化:
int pthread_relock_init(pthread_t* rlock,pthread_rwlockattr * attr);
3、读锁:
int pthread_rwlock_rdlock(pthread_rwlock_t *relock);
允许多个线程同时进行读加锁,所有线程都是执行读操作,不会改变数据
4、写锁:
int pthread_rwlock_wdlock(pthread_rwlock_t *relock);
int pthread_rwlock_udlock(pthread_rwlock_t *relock);
5、销毁:
int pthread_rwlock_destroypthread_rwlock_t *relock);
《UNIX环境高级编程》