线程的主要优势在于,能够通过全局变量来共享信息。不过,这种便捷的共享是有代价的:必须确保多个线程不会同时修改同一变量,或者某一线程不会读取正在由其他线程修改的变量
临界区是指访问某一共享资源的代码片段,并且这段代码的执行应为原子操作,也就是同时访问同一共享资源的其他线程不应中断该片段的执行
线程同步:即当有一个线程在对内存进行操作时,其他线程都不可以对这个内存地址进行操作,直到该线程完成操作,其他线程才能对该内存地址进行操作,而其他线程则处于等待状态
/*
使用多线程实现卖票案例
有3个窗口,一共是100张票
*/
#include
#include
#include
//全局变量,所有的线程共享这一份资源
int tickets = 100;
void *sellticket(void * arg){
//卖票
while(tickets > 0){
printf("%ld 正在卖第%d张门票\n",pthread_self(),tickets);
tickets--;
usleep(1);
}
return NULL;
}
int main(){
//创建3个子线程
pthread_t tid1,tid2,tid3;
pthread_create(&tid1,NULL,sellticket,NULL);
pthread_create(&tid2,NULL,sellticket,NULL);
pthread_create(&tid3,NULL,sellticket,NULL);
//回收子线程的资源,阻塞
pthread_join(tid1,NULL);
pthread_join(tid2,NULL);
pthread_join(tid3,NULL);
//设置线程分离
// pthread_detach(tid1);
// pthread_detach(tid2);
// pthread_detach(tid3);
pthread_exit(NULL);//退出主线程
return 0;
}
为避免线程共享变量时出现问题,可以使用互斥锁(mutex 是 mutual exclusion的缩写)来确保同时仅有一个线程可以访问某项共享资源。可以使用互斥锁来保证对任意共享资源的原子访问
互斥锁有两种状态:已锁定(locked)和未锁定(unlocked)。任何时候,至多只有一个线程可以锁定该互斥锁。试图对已经锁定的某一互斥锁再次加锁,将可能阻塞线程或者报错失败,具体取决于加锁时使用的方法
一旦线程锁定互斥锁,随机称为该互斥锁的所有者,只有所有者才能给互斥锁解锁。一般情况下,对每一共享资源(可能由多个相关变量组成)会使用不同的互斥锁,每一线程在访问同一资源时将采用如下协议:
int pthread_mutex_init(pthread_mutex_t * restrict mutex,const pthread_mutexattr_t *restrict attr);
int pthread_mutex_destroy(pthread_mutex_t *mutex);
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
#include
#include
#include
//全局变量,所有的线程共享这一份资源
int tickets = 100;
//创建一个互斥锁
pthread_mutex_t mutex;
void *sellticket(void * arg){
//卖票
while(1){
//加锁
pthread_mutex_lock(&mutex);
if(tickets > 0){
printf("%ld 正在卖第%d张门票\n",pthread_self(),tickets);
tickets--;
}else{
//解锁
pthread_mutex_unlock(&mutex);
break;
}
//解锁
pthread_mutex_unlock(&mutex);
}
return NULL;
}
int main(){
//初始化互斥锁
pthread_mutex_init(&mutex,NULL);
//创建3个子线程
pthread_t tid1,tid2,tid3;
pthread_create(&tid1,NULL,sellticket,NULL);
pthread_create(&tid2,NULL,sellticket,NULL);
pthread_create(&tid3,NULL,sellticket,NULL);
//回收子线程的资源,阻塞
pthread_join(tid1,NULL);
pthread_join(tid2,NULL);
pthread_join(tid3,NULL);
pthread_exit(NULL);//退出主线程
//释放互斥锁资源
pthread_mutex_destroy(&mutex);
return 0;
}
两个或两个以上的进程在执行过程中,因共享资源而造成的一种相互等待的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁
死锁的几种场景:
在对数据的读写操作中,更多的是读操作,写操作较少。为了满足当前能够允许多个读出,但只允许一个写入的需求,线程提供了读写所来实现
读写锁的特点:
读写锁的类型 pthread_rwlock_t
int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock,const pthread_rwlockattr_t *restrict attr);
int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock)
/*
案例: 8个线程操作同一个全局变量
3个线程不定时写一个全局变量,5个线程不定时的读一个全局变量
*/
#include
#include
#include
//创建一个共享数据
int num = 1;
pthread_rwlock_t rwlock;
void *writenum(void* arg){
while (1)
{
pthread_rwlock_wrlock(&rwlock);
num++;
printf("++write,tid : %ld,num : %d\n",pthread_self(),num);
pthread_rwlock_unlock(&rwlock);
usleep(100);
}
return NULL;
}
void *readnum(void *arg){
while(1){
pthread_rwlock_rdlock(&rwlock);
printf("===read,tid: %ld,num : %d\n",pthread_self(),num);
pthread_rwlock_unlock(&rwlock);
usleep(100);
}
return NULL;
}
int main(){
pthread_rwlock_init(&rwlock,NULL);
//创建3个写线程,5个读线程
pthread_t wtids[3],rtids[5];
for(int i = 0;i < 3;i++){
pthread_create(&wtids[i],NULL,writenum,NULL);
}
for(int i = 0;i < 5;i++){
pthread_create(&rtids[i],NULL,readnum,NULL);
}
//设置线程分离
for(int i = 0;i < 3;i++){
pthread_detach(wtids[i]);
}
for(int i = 0;i < 5;i ++){
pthread_detach(rtids[i]);
}
pthread_rwlock_destroy(&rwlock);
pthread_exit(NULL);
return 0;
}
条件变量的类型 pthread_cond_t
int pthread_cond_init(pthread_cond_t *restrict cond,const pthread_condattr_t *restrict attr);
int pthread_cond_destroy(pthread_cond_t *cond);
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);
#include
#include
#include
#include
//创建一个互斥锁
pthread_mutex_t mutex;
//创建条件变量
pthread_cond_t cond;
struct Node
{
int num;
struct Node* next;
};
//头节点
struct Node * head = NULL;
void * producer(void * arg){
//不断的创建新的节点,添加到链表中
while(1){
pthread_mutex_lock(&mutex);
struct Node * newNode = (struct Node*)malloc(sizeof(struct Node));
newNode->next = head;
head = newNode;
newNode->num = rand() % 1000;
printf("add node,num : %d,tid : %ld\n",newNode->num,pthread_self());
//只要生产了一个,就通知消费者消费
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mutex);
usleep(100);
}
return NULL;
}
void *customer(void * arg){
while (1)
{
pthread_mutex_lock(&mutex);
//保存头节点的指针
struct Node* tmp = head;
//判断是否有数据
if(head != NULL){
//有数据
head = head->next;
printf("del node,num : %d,tid : %ld\n",tmp->num,pthread_self());
free(tmp);
pthread_mutex_unlock(&mutex);
usleep(100);
}else{
//没有数据,需要等待
//当这个函数调用阻塞的时候,会对互斥锁进行解锁,当不阻塞的,继续向下执行,会重新加锁
pthread_cond_wait(&cond,&mutex);
pthread_mutex_unlock(&mutex);
}
}
return NULL;
}
int main(){
pthread_mutex_init(&mutex,NULL);
pthread_cond_init(&cond,NULL);
//创建5个生产者线程和5个消费这线程
pthread_t ptids[5],ctids[5];
for(int i = 0 ;i < 5;i++){
pthread_create(&ptids[i],NULL,producer,NULL);
pthread_create(&ctids[i],NULL,customer,NULL);
}
for(int i = 0;i < 5;i ++){
pthread_detach(ptids[i]);
pthread_detach(ctids[i]);
}
while(1){
sleep(10);
}
pthread_mutex_destroy(&mutex);
pthread_cond_destroy(&cond);
pthread_exit(NULL);
return 0;
}
信号量的类型 sem_t
int sem_init (sem_t *sem,int pshared,unsigned int value);
int sem_destroy(sem_t *sem);
int sem_wait(sem_t *sem):
int sem_trywait(sem_t *sem);
int sem_timedwait(sem_t *sem,const struct timespec *abs_timeout);
int sem_post(sem_t *sem);
int sem_getvalue(sem_t *sem,int *sval);
#include
#include
#include
#include
#include
//创建一个互斥锁
pthread_mutex_t mutex;
//创建两个信号量
sem_t psem,csem;
struct Node
{
int num;
struct Node* next;
};
//头节点
struct Node * head = NULL;
void * producer(void * arg){
//不断的创建新的节点,添加到链表中
while(1){
sem_wait(&psem);
pthread_mutex_lock(&mutex);
struct Node * newNode = (struct Node*)malloc(sizeof(struct Node));
newNode->next = head;
head = newNode;
newNode->num = rand() % 1000;
printf("add node,num : %d,tid : %ld\n",newNode->num,pthread_self());
pthread_mutex_unlock(&mutex);
sem_post(&csem);
}
return NULL;
}
void *customer(void * arg){
while (1)
{
sem_wait(&csem);
pthread_mutex_lock(&mutex);
//保存头节点的指针
struct Node* tmp = head;
head = head->next;
printf("del node,num : %d,tid : %ld\n",tmp->num,pthread_self());
free(tmp);
pthread_mutex_unlock(&mutex);
sem_post(&psem);
}
return NULL;
}
int main(){
pthread_mutex_init(&mutex,NULL);
sem_init(&psem,0,8);
sem_init(&csem,0,0);
//创建5个生产者线程和5个消费这线程
pthread_t ptids[5],ctids[5];
for(int i = 0 ;i < 5;i++){
pthread_create(&ptids[i],NULL,producer,NULL);
pthread_create(&ctids[i],NULL,customer,NULL);
}
for(int i = 0;i < 5;i ++){
pthread_detach(ptids[i]);
pthread_detach(ctids[i]);
}
while(1){
sleep(10);
}
pthread_mutex_destroy(&mutex);
pthread_exit(NULL);
return 0;
}