mutex锁的创建和销毁
mutex锁的属性
加锁、解锁、测试加锁
线程的同步
信号量是system V标准,线程库是prosix标准。每一个标准都觉得自己牛,就自己写一套。写进程用前面的信号量、写线程用锁。
linux锁的类型:
1.自旋锁:主要用在内核调度中。spinlock。自旋锁的效率更高。不睡觉,二是不停地看锁有没有打开。嵌入式开发采用。
2.互斥锁(睡眠锁):信号量、mutex。存在睡觉和唤醒的消耗。节省CPU资源,但是效率较低。
信号量:pv操作
mutex:加解锁
int pthread_mutex_init(pthread_mutex_t *
mutex, const pthread_mutexattr_t *mutexattr);
//mutexattr用于指定互斥锁属性,如果为NULL则
使用缺省属性。通常为NULL。
一、mutex锁的创建和销毁
静态初始化:不用。一般用动态初始化。
例:
int main(){
pthread_mutex_t
mutex1;
int ret;d
ret=
pthread_mutex_init(
&mutex1,NULL);
//创建锁
if(ret!=0){
printf("pthread_mutex_init failed ret=%d\n",ret);
return -1;
}
printf("mutex init success\n");
ret=
pthread_mutex_destroy(
&mutex1);
//销毁锁
if(ret!=0){
printf("pthread_mutex_destroy failed ret=%d\n",ret);
return -1;
}
return 0;
}
二、mutex锁的属性
互斥锁的属性结构体为:
typedef struct{
int __mutexkind;
//注意这里是两个下划线。在LinuxThreads实现中仅有一个锁类型属性__mutexkind.这个成员的值主要有三种。
}pthread_mutexattr_t;
PTHREAD_MUTEX_TIMED_NP
//缺省值。默认的情况下:单个线程加锁两次,第二次加锁会卡着
PTHREAD_MUTEX_RECURSIVE_NP
//嵌套锁。可以进行多次加锁。
PTHREAD_MUTEX_ERRORCHECK_NP
//检错锁。也没啥用。当加锁多次的时候,第二次加锁,会返回EDEADLK。
初始化方法:
方法一: //这种方法存在一个问题,就是__mutexkind这个变量,一旦变了之后,需要找他变成了什 么,再自己改。
pthread_mutex_t lock;
pthread_mutexattr_t
mutexattr;
mutexattr.__mutexkind = PTHREAD_MUTEX_RECURSIVE_NP;
pthread_mutex_init(&lock, &
mutexattr);
方法二:
//
用函数进行初始化,
比较暴力的初始化方法:memcpy.直接往结构体里面拷。
pthread_mutexattr_t
mutexattr;
int i ;
i=PTHREAD_MUTEX_ERRORCHECK_NP;
memcpy(
&mutexattr,
&i,sizeof(int));
方法三:
//这种方法比较常用。
pthread_mutexattr_t
mattr; //建立互斥锁的属性结构体
pthread_mutexattr_init(
&mattr); //对属性结构体进行初始化
pthread_mutexattr_settype(
&mattr,
PTHREAD_MUTEX_ERRORCHECK_NP); //改变属性
例子1:默认情况下,同一把锁加锁两次。会产生死锁。第二次加锁会卡着。
int main(){
pthread_mutex_t m1;
int ret;
ret=pthread_mutex_init(&m1,
NULL);
//默认情况
if(ret!=0){
printf("pthread_mutex_init failed,ret=%d\n",ret);
return -1;
}
pthread_mutex_lock(&m1);
//第一次加锁
printf("I lock success\n");
pthread_mutex_lock(&m1);
//第二次加锁,产生死锁
printf("I lock twice\n");
return 0;
}
例子2:用第三种方法,改变锁的属性为嵌套锁。
int main(){
pthread_mutex_t m1;
pthread_mutexattr_t mattr;
int ret;
pthread_mutexattr_init(&mattr);
ret=
pthread_mutexattr_settype(
&mattr,
PTHREAD_MUTEX_RECURSIVE_NP);
if(ret!=0){
printf("pthread_mutexattr_setpshared failed,ret=%d\n",ret);
return -1;
}
ret=
pthread_mutex_init(
&m1,
&mattr);
if(ret!=0){
printf("pthread_mutex_init failed,ret=%d\n",ret);
return -1;
}
pthread_mutex_lock(&m1);
//可以加锁,输出I lock success
printf("I lock success\n");
ret=
pthread_mutex_lock(&m1);
//还可以加锁,输出ret=0,I lock twice.
printf("ret=%d\n",ret);
printf("I lock twice\n");
return 0;
}
例子3:
用第三种方法,改变锁的属性为检错锁。
int main(){
pthread_mutex_t m1;
pthread_mutexattr_t mattr;
int ret;
pthread_mutexattr_init(&mattr);
ret=pthread_mutexattr_settype(&mattr,
PTHREAD_MUTEX_ERRORCHECK_NP);
if(ret!=0){
printf("pthread_mutexattr_setpshared failed,ret=%d\n",ret);
return -1;
}
ret=pthread_mutex_init(&m1,&mattr);
if(ret!=0){
printf("pthread_mutex_init failed,ret=%d\n",ret);
return -1;
}
pthread_mutex_lock(&m1);
//可以加锁,输出I lock success
printf("I lock success\n");
ret=
pthread_mutex_lock(&m1);
//不可以加锁,返回值ret不是0,但是不会死锁,会继续执行,输出I lock twice.
printf("ret=%d\n",ret);
printf("I lock twice\n");
return 0;
}
三、加锁、解锁、测试加锁
int pthread_mutex_lock(pthread_mutex_t *mutex)
//加锁
int pthread_mutex_unlock(pthread_mutex_t *mutex)
//解锁
int pthread_mutex_trylock(pthread_mutex_t *mutex)
//测试加锁。因为加锁的时候,我加不上就会睡觉。但是有时我不想睡觉。如果 没锁,try之后会加锁。如果锁了,则返回错误EBUSY,但是不会睡觉,去干 别的事。有点像自旋锁。
例1:通过加解锁实现两个线程加4000万的操作
int t;
pthread_mutex_t
m1;
//定义锁。因为子线程和主线程都要用到,所以使用全局变量。
void* thread(void* p){
int i;
for(i=0;i<20000000;i++){
//子线程加2000万
pthread_mutex_lock(&m1);
t++;
pthread_mutex_unlock(&m1);
}
pthread_exit(NULL);
}
int main(){
t=0;
pthread_t pth_id;
int ret;
ret=
pthread_mutex_init(&m1,NULL);
//创建锁
if(ret!=0){
printf("pthread_mutex_init failed ret=%d\n",ret);
return -1;
}
pthread_create(&pth_id,NULL,
thread,NULL);
//创建线程
int i;
for(i=0;i<20000000;i++){
//主线程加2000万
pthread_mutex_lock(&m1);
//加锁
t++;
pthread_mutex_unlock(&m1);
//解锁
}
pthread_join(pth_id,NULL); //等自线程计算完。
printf("t=%d\n",t);
pthread_mutex_destroy(&m1); //销毁锁
return 0;
}
例2:pthread_mutex_trylock的使用场景,使用trylock尝试加锁失败,返回EBUSY错误码。如果之前没有锁上,则跟加锁一样,会加锁。如果之前有锁了,不会阻塞等待,而是返回错误。
int t;
pthread_mutex_t m1;
void* thread(void* p){
int i;
pthread_mutex_lock(&m1);
//子线程拿到锁之后,一直使用,不解锁。
printf("I get lock\n");
while(1);
pthread_exit(NULL);
}
int main(){
t=0;
pthread_t pth_id;
int ret;
ret=pthread_mutex_init(&m1,NULL);
if(ret!=0){
printf("pthread_mutex_init failed ret=%d\n",ret);
return -1;
}
pthread_create(&pth_id,NULL,thread,NULL);
int i;
sleep(1);
//先睡一觉,防止主线程抢到锁
ret=
pthread_mutex_trylock(&m1);
//如果这个地方是加锁的话,因为子线程拿着锁不放,所以主线程睡觉。但是这个
地方使用的是测试加锁,所以不会睡觉,而是返回的错误码。
printf("pthread_mutex_trylock ret =%d\n",ret);
//打印出错误码。
pthread_mutex_destroy(&m1);
return 0;
}
互斥:有个资源,你能用我不能用。加解锁。
同步:线程创建好了。现在通知你,醒来。
四、线程的同步:条件变量
条件变量是利用线程间共享的全局变量进行同步的一种机制,
主要包括两个动作:一个线程等待条件变量的条件成立而挂起;另一个
线程使条件成立(给出条件成立信号)。
条件变量要跟互斥锁一起使用。挂起的时候要排队,需要加锁。
条件变量的初始化和销毁
int main(){
pthread_cond_t
cond;
int ret;
ret=
pthread_cond_init(
&cond,NULL);
if(ret!=0){
printf("pthread_cond_init failed,ret=%d\n",ret);
return -1;
}
ret=
pthread_cond_destroy(
&cond);
//只有在没有线程在该条件变量上等待的时候能注销这个条件变量,否则返回EBUSY
if(ret!=0){
printf("pthread_cond_destroy failed,ret=%d\n",ret);
return -1;
}
return 0;
}ecsc
条件变量的等待、激发
int
pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
//条件变量的等待。
int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime);
//不会一直等下去,超时自己醒
来。
自己醒来还是被唤醒,返回值不一样。根据返回值判断去做什么事情。
pthread_cond_signal();
//激活一个等待该条件的线程,存在多个等待线程时按入队顺序激活其中一个
pthread_cond_broadcast();
//激活所有等待线程
pthread_cond_wait()做的事情:
线程睡觉前:
1.修改cond队列,我要等待该条件变量c
2.解锁mutex。这样别人就可以等了。等着的都在一个队列。
3.放弃CPU,睡觉。等待通知条件成立。
线程醒来:
1.重新对mutex加锁。
所以,一般这么写:
pthread_mutex_lock(&mutex);
pthread_cond_wait(&cond,&mutex); //在内部解锁,然后睡觉,然后加锁。
pthread_mutex_unlock(&mutex);
例1:条件变量的等待、激发
typedef struct {
//为了能够给线程同时传入两个参数,把两个参数放到一个结构体中。
pthread_cond_t
cond; //条件变量 cond
pthread_mutex_t
mutex; //mutex锁
}cm,*pcm;
void* thread(void* p){
pcm p1=
(pcm)p;
//将void型指针强转为结构体指针
int ret;
pthread_mutex_lock(&p1->mutex);
//等待就是要修改条件变量,因为不能同时修改,所以要加mutex锁。
printf("I will wait\n");
ret=
pthread_cond_wait(&p1->cond,&p1->mutex);
//解锁,睡觉等待唤醒,醒来后加锁.当醒来时,执行流程还在函数内部
printf("ret=%d\n",ret);
pthread_mutex_unlock(&p1->mutex);
pthread_exit(NULL);
}
int main(){
int ret;
cm p; //定义结构体局部变量,里边有一个锁,一个条件变量
ret=
pthread_cond_init(&p.cond,NULL);
//初始化条件变量
if(ret!=0){
printf("pthread_cond_init failed,ret=%d\n",ret);
return -1;
}
ret=
pthread_mutex_init(&p.mutex,NULL);
//创建锁
if(ret!=0){
printf("pthread_mutex_init failed,ret=%d\n",ret);
return -1;
}
pthread_t pth_id;
pthread_create(&pth_id,NULL,thread,
(void*)&p);
//创建子线程,并把锁和条件变量传进去
sleep(1);
//先睡一下,再唤醒,防止我唤醒的时候,子线程还没去排队。
ret=
pthread_cond_signal(&p.cond);
//激发条件变量,激活一个等待该条件的线程,
如果没有singnal的话,子线程
会等待。
if(ret!=0){
printf("pthread_cond_signal failed,ret=%d\n",ret);
return -1;
}
printf("I am main thread,I wait child\n");
pthread_join(pth_id,NULL);
ret=
pthread_cond_destroy(&p.cond);
if(ret!=0){
printf("pthread_cond_destroy failed,ret=%d\n",ret);
return -1;
}
return 0;
}
例2:通过boardcast 唤醒多个等待在同一条件变量的线程
typedef struct {
pthread_cond_t cond;
pthread_mutex_t mutex;
}cm,*pcm;
int j=0;
void* thread(void* p){
pcm p1=(pcm)p;
//将void型指针强转为结构体指针p1
int ret;
pthread_mutex_lock(&p1->mutex);
j++;
a=j;
printf("thread %d will wait\n",a);
//每次新建线程之后j++,打印j
ret=pthread_cond_wait(&p1->cond,&p1->mutex);
printf("thread %d wake\n",a);
pthread_mutex_unlock(&p1->mutex);
pthread_exit(NULL);
}
int main(){
int ret;
cm p;
ret=
pthread_cond_init(&p.cond,NULL);
//初始化条件变量
if(ret!=0){
printf("pthread_cond_init failed,ret=%d\n",ret);
return -1;
}
ret=
pthread_mutex_init(&p.mutex,NULL);
if(ret!=0){
printf("pthread_mutex_init failed,ret=%d\n",ret);
return -1;
}
pthread_t pth_id[5];
int i;
for(i=0;i<5;i++){
pthread_create(&pth_id[i],NULL,thread,(void*)&p);
//创建5个线程pth_id[0]-pth_id[4]
}
sleep(1);
ret=
pthread_cond_broadcast(&p.cond);
//激活所有等待线程
if(ret!=0){
printf("pthread_cond_signal failed,ret=%d\n",ret);
return -1;
}
printf("I am main thread,I wait child\n");
for(i=0;i<5;i++){
pthread_join(pth_id[i],NULL);
}
ret=
pthread_cond_destroy(&p.cond);
if(ret!=0){
printf("pthread_cond_destroy failed,ret=%d\n",ret);
return -1;
}
return 0;
}
例3:
typedef struct {
pthread_cond_t cond;
pthread_mutex_t mutex;
}cm,*pcm;
//子线程timedwait 等待条件成立,超时子线程自己醒来
void* thread(void* p){
pcm p1=(pcm)p;
int ret;
pthread_mutex_lock(&p1->mutex);
printf("I will wait\n");
struct timespec abstime;
memset(&abstime,0,sizeof(abstime));
abstime.tv_sec=time(NULL)+5;
//绝对时间是指当前时间+超时时间,不要写成5
ret=
pthread_cond_timedwait(&p1->cond,&p1->mutex,&abstime);
//超过5秒没有人唤醒会自动醒来。如果正常唤醒,就正常流程。
printf("ret=%d\n",ret);
pthread_mutex_unlock(&p1->mutex);
pthread_exit(NULL);
}
int main(){
int ret;
cm p;
//局部变量,里边有一个锁,一个条件变量
ret=
pthread_cond_init(&p.cond,NULL);
//初始化条件变量
if(ret!=0){
printf("pthread_cond_init failed,ret=%d\n",ret);
return -1;
}
ret=
pthread_mutex_init(&p.mutex,NULL);
//初始化锁
if(ret!=0){
printf("pthread_mutex_init failed,ret=%d\n",ret);
return -1;
}
pthread_t pth_id;
pthread_create(&pth_id,NULL,thread,
(void*)&p);
//创建线程,下面不写唤醒程序。
printf("I am main thread,I wait child\n");
pthread_join(pth_id,NULL);
ret=pthread_cond_destroy(&p.cond);
if(ret!=0){
printf("pthread_cond_destroy failed,ret=%d\n",ret);
return -1;
}
return 0;
}
find
怎么搜时间的结构体。14:37分。
防止在睡觉的时候被cancel掉,要加pop.弹出去不执行。
100M的电影,快速拷贝文件。通过多点拷贝。将文件切成10段。fseek.内存中分十段。读的时候mmap.写的时候feek.