线程的同步、互斥

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.


猜你喜欢

转载自blog.csdn.net/pengchengliu/article/details/80524873
今日推荐