热门标签 | HotTags
当前位置:  开发笔记 > 运维 > 正文

线程的同步、互斥

mutex锁的创建和销毁mutex锁的属性加锁、解锁、测试加锁线程的同步信号量是systemV
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.



推荐阅读
  • Nginx使用AWStats日志分析的步骤及注意事项
    本文介绍了在Centos7操作系统上使用Nginx和AWStats进行日志分析的步骤和注意事项。通过AWStats可以统计网站的访问量、IP地址、操作系统、浏览器等信息,并提供精确到每月、每日、每小时的数据。在部署AWStats之前需要确认服务器上已经安装了Perl环境,并进行DNS解析。 ... [详细]
  • Linuxchmod目录权限命令图文详解在Linux文件系统模型中,每个文件都有一组9个权限位用来控制谁能够读写和执行该文件的内容。对于目录来说,执行位的作用是控制能否进入或者通过 ... [详细]
  • 近年来,大数据成为互联网世界的新宠儿,被列入阿里巴巴、谷歌等公司的战略规划中,也在政府报告中频繁提及。据《大数据人才报告》显示,目前全国大数据人才仅46万,未来3-5年将出现高达150万的人才缺口。根据领英报告,数据剖析人才供应指数最低,且跳槽速度最快。中国商业结合会数据剖析专业委员会统计显示,未来中国基础性数据剖析人才缺口将高达1400万。目前BAT企业中,60%以上的招聘职位都是针对大数据人才的。 ... [详细]
  • 本文介绍了数据库的存储结构及其重要性,强调了关系数据库范例中将逻辑存储与物理存储分开的必要性。通过逻辑结构和物理结构的分离,可以实现对物理存储的重新组织和数据库的迁移,而应用程序不会察觉到任何更改。文章还展示了Oracle数据库的逻辑结构和物理结构,并介绍了表空间的概念和作用。 ... [详细]
  • Linux重启网络命令实例及关机和重启示例教程
    本文介绍了Linux系统中重启网络命令的实例,以及使用不同方式关机和重启系统的示例教程。包括使用图形界面和控制台访问系统的方法,以及使用shutdown命令进行系统关机和重启的句法和用法。 ... [详细]
  • 本文主要讨论了在xps15上安装双系统win10和MacOS后,win10无法正常更新的问题。分析了可能的引导问题,并提供了解决方法。 ... [详细]
  • Centos7.6安装Gitlab教程及注意事项
    本文介绍了在Centos7.6系统下安装Gitlab的详细教程,并提供了一些注意事项。教程包括查看系统版本、安装必要的软件包、配置防火墙等步骤。同时,还强调了使用阿里云服务器时的特殊配置需求,以及建议至少4GB的可用RAM来运行GitLab。 ... [详细]
  • 本文介绍了在Hibernate配置lazy=false时无法加载数据的问题,通过采用OpenSessionInView模式和修改数据库服务器版本解决了该问题。详细描述了问题的出现和解决过程,包括运行环境和数据库的配置信息。 ... [详细]
  • 树莓派Linux基础(一):查看文件系统的命令行操作
    本文介绍了在树莓派上通过SSH服务使用命令行查看文件系统的操作,包括cd命令用于变更目录、pwd命令用于显示当前目录位置、ls命令用于显示文件和目录列表。详细讲解了这些命令的使用方法和注意事项。 ... [详细]
  • Metasploit攻击渗透实践
    本文介绍了Metasploit攻击渗透实践的内容和要求,包括主动攻击、针对浏览器和客户端的攻击,以及成功应用辅助模块的实践过程。其中涉及使用Hydra在不知道密码的情况下攻击metsploit2靶机获取密码,以及攻击浏览器中的tomcat服务的具体步骤。同时还讲解了爆破密码的方法和设置攻击目标主机的相关参数。 ... [详细]
  • Python语法上的区别及注意事项
    本文介绍了Python2x和Python3x在语法上的区别,包括print语句的变化、除法运算结果的不同、raw_input函数的替代、class写法的变化等。同时还介绍了Python脚本的解释程序的指定方法,以及在不同版本的Python中如何执行脚本。对于想要学习Python的人来说,本文提供了一些注意事项和技巧。 ... [详细]
  • 本文介绍了Oracle数据库中tnsnames.ora文件的作用和配置方法。tnsnames.ora文件在数据库启动过程中会被读取,用于解析LOCAL_LISTENER,并且与侦听无关。文章还提供了配置LOCAL_LISTENER和1522端口的示例,并展示了listener.ora文件的内容。 ... [详细]
  • 本文详细介绍了Linux中进程控制块PCBtask_struct结构体的结构和作用,包括进程状态、进程号、待处理信号、进程地址空间、调度标志、锁深度、基本时间片、调度策略以及内存管理信息等方面的内容。阅读本文可以更加深入地了解Linux进程管理的原理和机制。 ... [详细]
  • 图解redis的持久化存储机制RDB和AOF的原理和优缺点
    本文通过图解的方式介绍了redis的持久化存储机制RDB和AOF的原理和优缺点。RDB是将redis内存中的数据保存为快照文件,恢复速度较快但不支持拉链式快照。AOF是将操作日志保存到磁盘,实时存储数据但恢复速度较慢。文章详细分析了两种机制的优缺点,帮助读者更好地理解redis的持久化存储策略。 ... [详细]
  • 本文介绍了在Linux下安装Perl的步骤,并提供了一个简单的Perl程序示例。同时,还展示了运行该程序的结果。 ... [详细]
author-avatar
东yidd_154
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有