目录
线程同步:
1.概述
2.线程同步的方式
1.互斥锁:
2.读写锁:
3.进程互斥锁:
4.自旋锁:
5.信号量:
6.条件变量COND:
线程同步:
1.概述
多线程模型中,即当有一个线程在对内存进行操作时,其他线程都不可以对这个内存地址进行操作,直到该线程完成操作, 其他线程才能对该内存地址进行操作,而其他线程又处于等待状态。
需要安装:
sudo apt-get install manpages-posix
sudo apt-get install manpages-posix-dev
在man里面可以查询到线程同步的相关函数
2.线程同步的方式
1.互斥锁:
可以使用互斥锁对共享资源进行保护
老版本的互斥锁,占用线程解除占用,内核唤醒全部等待线程,多个线程竞争,获取锁资源(惊群效应)
内核会给等待队列中某个线程发送唤醒标记,内定下一个资源使用的线程 占用线程解除占用后,内核会确认队列,确认队列后发送唤醒标记(就近原则) 注意:某个线程持续占用,重复申请,那么大概率该线程一直占用锁资源
互斥锁涉及的函数
pthread_mutex_t lock //互斥锁类型,
pthread_mutex_init(pthread_mutex_t * lock,pthread_mutexattr_t * mattr); //互斥锁初始化
pthread_mutex_destroy(pthread_mutex_t * lock); //互斥锁销毁
pthread_mutex_lock(pthread_mutex_t * lock);//互斥锁加锁
pthread_mutex_unlock(pthread_mutex_t * lock);//互斥锁解锁
只在全局资源访问时加锁
示例:
两个线程同时执行100次 ++,执行结果不一定是200。因为会有线程并发的问题。
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <sys/wait.h>
#include <unistd.h>
int ncount = 0;
pthread_mutex_t mlock;
void* thread_joba(void *lpvoid)
{int i =0;while(i++ <5000){usleep(100);pthread_mutex_lock(&mlock);printf("thread_joba is %x,++ncount = %d\n",(unsigned int)pthread_self(),ncount++);pthread_mutex_unlock(&mlock);}
}
void* thread_jobb(void* lpvoid)
{int i =0;while(i++ <5000){pthread_mutex_lock(&mlock);printf("thread_jobb is %x,++ncount = %d\n",(unsigned int)pthread_self(),ncount++);pthread_mutex_unlock(&mlock);usleep(100);}
}
int main()
{pthread_t tid1,tid2;pthread_mutex_init(&mlock,NULL);pthread_create(&tid1,NULL,thread_joba,NULL);pthread_create(&tid2,NULL,thread_jobb,NULL);pthread_join(tid1,NULL);pthread_join(tid2,NULL);pthread_mutex_destroy(&mlock);return 0;
}
2.读写锁:
可以提高资源访问率,读写锁中写锁为(独占锁)只允许一个线程获取写锁,对全局资源进行写操作,读锁(共享锁)若干个线程可以获取读锁,对全局资源进行读访问 读写锁使用时,读写互斥(进行读操作时,不允许进行写操作,反之亦然)
读写锁涉及到的函数
pthread_rwlock_t lock //读写锁类型
pthread_rwlock_init(pthread_rwlock_t *lock,NULL) //初始化读写锁
pthread_rwlock_destroy(pthread_rwlock_t *lock) //销毁读写锁
pthread_rwlock_wrlock(pthread_rwlock_t *lock) //申请用写锁(独占锁)
pthread_rwlock_rdlock(pthread_rwlock_t *lock) //申请用读锁(共享锁)
pthread_rwlock_unlock(pthread_rwlock_t* lock) //解锁(独占或共享)
示例: 创建8个线程,共享使用一把读写锁,三个写线程使用写锁操作共享资源,五个读线程读访问共享资源
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <sys/wait.h>
#include <unistd.h>
int ncount = 0;
pthread_rwlock_t rwlock;
void* thread_joba(void *lpvoid)
{int i =0;while(i++ <5000){usleep(100);pthread_rwlock_rdlock(&rwlock);printf("thread_joba is %x,read ncount = %d\n",(unsigned int)pthread_self(),ncount);pthread_rwlock_unlock(&rwlock);}
}
void* thread_jobb(void* lpvoid)
{int i =0;while(i++ <5000){pthread_rwlock_wrlock(&rwlock);printf("thread_jobb is %x, write ncount = %d\n",(unsigned int)pthread_self(),ncount++);pthread_rwlock_unlock(&rwlock);usleep(100);}
}
int main()
{int i =0;pthread_t tid1,tid2;pthread_rwlock_init(&rwlock,NULL);for(i;i<3;i++){pthread_create(&tid1,NULL,thread_joba,NULL);}for(i=3;i<8;i++){pthread_create(&tid2,NULL,thread_jobb,NULL);}pthread_join(tid1,NULL);pthread_join(tid2,NULL);pthread_rwlock_destroy(&rwlock);return 0;
}
使用场合:log日志系统
快速定位错误异常,有人要写日志, 有人要读日志(排查异常)
3.进程互斥锁:
互斥锁默认情况下使用为线程互斥锁,可以通过设置互斥锁属性,将lock变为进程互斥锁,适用于多进程保护全局资源
pthread_mutexattr_t attr; //互斥锁属性类型
pthread_mutexattr_init(pthread_mutexattr_t *attr); //初始化互斥锁属性。默认时线程互斥锁
pthread_mutexattr_destroy(pthread_mutexattr_t* attr); //销毁释放互斥锁属性
//通过API将attr中的线程互斥锁属性改为进程互斥锁属性
pthread_mutexattr_setpshared(pthread_mutexattr_t* attr,int* pshared);//修改互斥锁属性中的互斥锁类型
pthread_mutexattr_getpshared(pthread mutexattr_t* attr,int* pshared);//获取互斥锁属性中的锁类型
pshared 锁类型:PTHREAD_PROCESS_SHARED //进程互斥锁PTHREAD_PROCESS_PRIVATE //线程互斥锁
pthread_mutex_init(&lock,&attr); //互斥锁属性准备完成后,在互斥锁初始化时使用
示例: 父子进程利用进程锁,保护全局资源(实现资源访问互斥)
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define onepage 4096
typedef struct myshared
{pthread_mutex_t lock;int counter;
}myshared;
int main()
{ pid_t pid;//创建一个用于共享内存映射得文件int fd = open("mmapfile",O_RDWR|O_CREAT,0664);if(fd == -1){printf("open failed \n");exit(0);}//将文件设置为myshared大小ftruncate(fd,sizeof(myshared));myshared *pshared = (myshared *)mmap(NULL,sizeof(myshared),PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);//设置互斥锁属性,变为进程互斥锁pthread_mutexattr_t lockattr;pthread_mutexattr_init(&lockattr);pthread_mutexattr_setpshared(&lockattr,PTHREAD_PROCESS_SHARED);pthread_mutex_init(&(pshared->lock),&lockattr);pid = fork();int i =0;if(pid >0){for(i=0;i<50;i++){pthread_mutex_lock(&pshared->lock);printf("parent process counter= %d\n",++pshared->counter);pthread_mutex_unlock(&pshared->lock);}}else if(pid ==0){for(i =0;i<50;i++){ pthread_mutex_lock(&pshared->lock);printf("child process counter= %d\n",++pshared->counter);pthread_mutex_unlock(&pshared->lock);}}else{printf("fork failed\n");exit(0);}wait(NULL);pthread_mutex_destroy(&pshared->lock);pthread_mutexattr_destroy(&lockattr);munmap(pshared,sizeof(myshared));return 0;
}
4.自旋锁:
积极锁(不会阻塞),当某线程占用锁资源时,其他线程会不停自旋尝试获取锁资源,直到占用线程释放为止 优势:自旋锁可以提高资源的利用率,相比互斥锁可以更快的获取资源(与互斥锁不同没有一系列挂起唤醒操作,减少开销) 弊端:使用自旋锁的线程会长时间处于R状态,进线程开销较大
DEFINE_SPINLOCK(spinlock_t lock) 定义并初始化化一个自旋锁int spin_lock_init(spinlock_t *lock) 初始化自旋锁void spin_lock(spinlock_t *lock) 获取指定的自旋锁 即加锁void spin_unlock(spinlock_t *lock) 释放自旋锁 或 解锁int spin_trylock(spinlock_t *lock) 尝试获取指定的自旋锁,如果没有获取到就返回 0int spin_is_locked(spinlock_t *lock) 检查指定的自旋锁是否被获取,如果没有被获取就返回非 0,否则返回 0void spin_lock_irq(spinlock_t *lock) 禁止本地中断,并获取自旋锁void spin_unlock_irq(spinlock_t *lock) 激活本地中断,并释放自旋锁void spin_lock_irqsave(spinlock_t *lock,unsigned long flags) 保存中断状态,禁止本地中断,并获取自旋锁void spin_unlock_irqrestore(spinlock_t*lock, unsigned long flags)将中断状态恢复到以前的状态,并且激活本地中断,释放自旋锁 自旋锁简单使用 1 DEFINE_SPINLOCK(lock) 定义并初始化一个自旋锁/* 线程 A */void functionA (){unsigned long flags; /* 中断状态 */spin_lock_irqsave(&lock, flags) /* 获取锁 *//* 临界区 */spin_unlock_irqrestore(&lock, flags) /* 释放锁 */}
/* 中断服务函数 */void irq() {spin_lock(&lock) /* 获取锁 *//* 临界区 */spin_unlock(&lock) /* 释放锁 */}
5.信号量:
略
6.条件变量COND:
常见的一些线程并发模型中,多线程无关联,各自执行完成任务,条件变量可以通过指定条件,逻辑让多线程有一个交集场景,让开发者可以通过条件判断,控制工作线程
条件变量可以控制线程的挂起或唤醒(开发者利用条件制定规则,线程严格按照条件规则或执行任务,或等待)
pthread_cond_t cd//条件变量类型
pthread_cond_init(pthread_cond_t *cd,NULL);//初始化条件变量
pthread_cond_destroy(pthread_cond_t *cd ) ;
pthread_cond_wait(pthread_cond_t* cd, pthread_mutex_t *lock); //通过此函数,可以将线程挂起在特定的条件变量上
pthread_cond_signal(pthread_cond_t *cd); //唤醒一个挂起在指定条件变量中的线程
pthread_cond_broadcast(pthread_cond_t* cd) //唤醒所有挂起在指定条件变量中的线程
示例:两个线程,对全局资源counter进行++操作,,但是区分工作,分奇数偶数
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/stat.h>
pthread_mutex_t mlock;
pthread_cond_t ca;
pthread_cond_t cb;
int ncounter = 0;
void* thread_job1(void* lpvoid)
{int i= 0;for(i;i <100;i++){pthread_mutex_lock(&mlock);if(ncounter %2 ==0){pthread_cond_wait(&ca,&mlock);}printf("thread job1 %x ncounter = %d\n",(unsigned int)pthread_self(),++ncounter);pthread_cond_signal(&cb);pthread_mutex_unlock(&mlock);}
}
void* thread_job2(void* lpvoid)
{int i= 0;for(i;i <100;i++){pthread_mutex_lock(&mlock);if(ncounter %2 !=0){pthread_cond_wait(&cb,&mlock);}printf("thread job2 %x ncounter = %d\n",(unsigned int)pthread_self(),++ncounter);pthread_cond_signal(&ca);pthread_mutex_unlock(&mlock);}
}
int main()
{ pthread_t tid1,tid2;pthread_mutex_init(&mlock,NULL);pthread_cond_init(&ca,NULL);pthread_cond_init(&cb,NULL);pthread_create(&tid1,0,thread_job1,0);pthread_create(&tid2,0,thread_job2,0);pthread_join(tid1,NULL);pthread_join(tid2,NULL);pthread_mutex_destroy(&mlock);pthread_cond_destroy(&ca);pthread_cond_destroy(&cb);return 0;
}
pthread_cond_wait函数,第一次调用(挂起调用),挂起当前线程并解锁互斥锁 第二次调用(唤醒调用),上锁互斥锁