• 线程同步


    一、互斥量

    互斥量从本质上说是一把锁,在访问共享资源前对互斥量进行加锁,在访问完成后释放互斥量上的锁。

    互斥量用pthread_mutex_t数据类型表示,在使用互斥变量以前,必须首先对它进行初始化。可以把它设置为常量PTHREAD_MUTEX_INITIALIZER(只对静态分配的互斥量),也可以通过调用pthread_mutex_init函数进行初始化,如果动态地分配互斥量(比如说malloc)那么在释放内存前需要调用pthread_mutex_destroy

    #include <pthread.h>
    int pthread_mutex_init(pthread_mutex_t *restrict mutex,const pthread_mutexattr_t *restrict attr);
    int pthread_mutex_destroy(pthread_mutex_t *mutex)
    //返回值:若成功返回0,否则返回错误编号

    对互斥量进行加锁,需要调用pthread_mutex_lock,如果互斥量已经上锁,调用线程将阻塞直到互斥量被解锁,对互斥量解锁,需要调用pthread_mutex_unlock

    #include <pthread.h>
    int pthread_mutex_lock(pthread_mutex_t *mutex)
    int pthread_mutex_trylock(pthread_mutex_t *mutex)
    int pthread_mutex_unlock(pthread_mutex_t *mutex)
    //返回值:若成功返回0,否则返回错误编号

    如果线程不希望被阻塞,它可以使用pthread_mutex_trylock尝试对互斥量进行加锁,如果调用pthread_mutex_trylock时互斥量处于未锁住状态,那么pthread_mutex_trylock将锁住互斥量,不会出现阻塞并返回0,否则pthread_mutex_trylock就会失败,不能锁住互斥量,而返回EBUSY。

    struct foo
    {
        int f_count;
        pthread_mutex_t f_lock;  
    };
    
    struct foo *foo_alloc(void)
    {
        struct foo *fp;
        if((fp=malloc(sizeof(struct foo)))!=NULL)//动态地分配互斥量
        {
            fp->count=1;
            if(pthread_mutex_init(&fp->f_lock,NULL)!=0)
            {
                free(fp);
                return(NULL);
            }
        }
        return(fp);
    }
    
    void foo_hold(struct foo *fp)
    {
        pthread_mutex_lock(&fp->f_lock);
        fp->f_count++;
        pthread_mutex_unlock(&fp->f_lock);
    }
    
    void foo_rele(struct foo *fp)
    {
        pthread_mutex_lock(&fp->f_lock);
        if(--fp->f_count==0)
        {
            pthread_mutex_unlock(&fp->f_lock);
            pthread_mutex_destroy(&fp->f_lock);
            free(fp);
        }
        else 
            pthread_mutex_unlock(&fp->f_lock);
    }

    二、避免死锁

    1.控制互斥量加锁顺序:总是让多个互斥量以相同的顺序加锁

    2.合理安排锁的功能:如果锁的粒度太粗,就会出现很多线程阻塞等待相同的锁;如果锁的粒度太细,那么过多的锁开销会使系统性能受到影响,而且代码变得复杂。

    三、读写锁

    读写锁pthread_rwlock_t可以有三种状态:读模式下加锁状态,写模式下加锁状态,不加锁状态。一次只有一个线程可以占有写模式的读写锁pthread_rwlock_wrlock,但是多个线程可以同时占有读模式的读写锁pthread_rwlock_rdlock。

    读写锁在使用之前必须初始化,在释放它们底层的内存前必须销毁。

    #include <pthread.h>
    int pthread_rwlock_init(pthread_rwlock_t *restrict rwllock,const pthread_rwlockattr_t *restrict attr);;
    int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
    #include <pthread.h>
    int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);//读模式下锁定读写锁
    int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);//写模式下锁定读写锁
    int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);//解锁

    四、条件变量

    条件变量pthread_cond_t通常被保存为全局变量,并和互斥锁合作,还需要和另一个全局变量 配合,这个全局变量用来构成各个条件。条件由互斥量保护,线程在改变条件状态前必须首先锁住互斥量。

    条件变量在使用之前必须进行初始化,可以把常量PTHREAD_COND_INITIALIZER赋给静态分配的条件变量,但是如果条件变量是动态分配的,可以使用pthread_cond_init函数初始化。

    使用pthread_cond_wait等待条件变为真,如果在给定时间内条件不能满足,会返回一个代表出错码的返回变量

    int pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex);

    pthread_cond_signal函数将唤醒等待该条件的某个线程,而pthread_cond_broadcast函数将唤醒等待该条件的所有线程。

    int pthread_cond_signal(pthread_cond_t *cond);

    int pthread_cond_broadcast(pthread_cond_t *cond);

    struct msg
    {
            struct msg *m_next;
    };
    
    struct msg *workq;//该全局变量构成各个条件
    pthread_cond_t qready=PTHREAD_COND_INITIALIZER;
    pthread_mutex_t qlock=PTHREAD_MUTEX_INITIALIZER;
    
    void process_msg(void)
    {
            struct msg *mp;
            for(;;)
            {
                    pthread_mutex_lock(&qlock);
                    while(workq==NULL)//当workq为NULL时,等待cond通知
                            pthread_cond_wait(&qready,&qlock);
                    mp=workq;
                    workq=mp->m_next;
                    pthread_mutex_unlock(&qlock);
            }
    }
    void enqueue_msg(struct msg *mp)
    {
            pthread_mutex_lock(&qlock);
            mp->m_next=workq;
            workq=mp;
            pthread_mutex_unlock(&qlock);
            pthread_cond_signal(&qready);
    }

     个人感觉条件变量的作用在于发送消息通知程序条件已经满足,继续下面的程序,互斥量保护全局变量构成的条件,当条件不满足时,使用条件变量对应的函数来阻塞条件。

  • 相关阅读:
    centos7开启关闭防火墙
    虚拟机vmnet1和vmnet8找不到,注册表没有删除干净,见图
    dhcp和static的区别
    SQL手册
    canal架构原理
    SQL优化(待完善)
    数仓简介
    java逆变与协变(待完善)
    mysqljoin的原理和优化
    深入理解java虚拟机阅读笔记
  • 原文地址:https://www.cnblogs.com/ljygoodgoodstudydaydayup/p/3822290.html
Copyright © 2020-2023  润新知