zoukankan      html  css  js  c++  java
  • Linux下C的线程同步机制

    C里提供了保证线程安全性的三种方法:

    (添加头文件#include<pthread.h>,pthread 库不是 Linux 系统默认的库,连接时需要使用静态库 libpthread.a, 在编译中要加 -lpthread参数)

    • 互斥锁

      通过锁的机制实现线程间的互斥,同一时刻只有一个线程可以锁定它,当一个锁被某个线程锁定的时候,如果有另外一个线程尝试锁定这个临界区(互斥体),则第二个线程会被阻塞,或者说被置于等待状态。只有当第一个线程释放了对临界区的锁定,第二个线程才能从阻塞状态恢复运行。

      int pthread_mutex_init(pthread_mutex_t* mutex, const thread_mutexattr_t* mutexattr);初始化一个互斥锁。

      int pthread_mutex_lock(pthread_mutex_t* mutex);如果mutex被锁定,当前进程处于等待状态;否则,本进程获得互斥锁并进入临界区。

      int pthread_mutex_trylock(pthread_mutex_t* mutex);和lock不同的时候,尝试获得互斥锁不成功不会使得进程进入阻塞状态,而是继续返回线程执行。该函数可以有效避免循环等待锁,如果trylock失败可以释放已经占有的资源,这样可以避免死锁

      int pthread_mutex_unlock(pthread_mutex_t* mutex);释放互斥锁,并使得被阻塞的线程获得互斥锁并执行。

      int pthread_mutex_destroy(pthread_mutex_t* mutex);用来撤销互斥锁的资源。

    pthread_mutex_t mutex;
    pthread_mutex_init(&mutex,NULL);
    
    
    void pthread1(void* arg){
       pthread_mutex_lock(&mutex);
       .....//临界区
       pthread_mutex_unlock(&mutex);
    }
    
    
    void pthread2(void* arg){
       pthread_mutex_lock(&mutex);
       .....//临界区
       pthread_mutex_unlock(&mutex);
    }
    • 读写锁

    读写锁与互斥量类似,不过读写锁允许更高的并行性。适用于读的次数大于写的次数的数据结构。

    一次只有一个线程可以占有写模式的读写锁,但是多个线程可以同时占有读模式的读写锁。

    读锁锁住,加读锁,可以;加写锁会被阻塞,但此时会阻塞后续的读锁请求,防止读锁长期占用无法进入写模式。写锁就是互斥锁。

    int pthread_rwlock_init(pthread_rwlock_t* rwlock, const pthread_rwlockattr_t* attr);初始化读写锁

    int pthread_destroy(pthread_rwlock_t* rwlock);销毁读写锁

    int pthread_rwlock_rdlock(pthread_rwlock_t* rwlock);加读锁

    int pthread_rwlock_wrlock(pthread_rwlock_t* rwlock);加写锁

    int pthread_rwlock_unlock(pthread_rwlock_t* rwlock);解锁

    • 条件变量

      信号量只有锁住和不锁两种状态,而且当条件变量和信号量一起使用时,允许线程以无竞争的方式等待特定的条件发生

      条件本身是由互斥量保护的:线程在改变条件状态之前必须先锁住互斥量。

      int pthread_cond_init(pthread_cond_t* cond,const pthread_condattr_t* attr);初始化动态分配的条件变量;也可以直接用PTHREAD_INITIALIZER直接赋值给静态的条件变量

      int pthread_cond_destroy(pthread_cond_t* cond)撤销条件变量资源;

      int pthread_cond_wait(pthread_cond_t* cond, pthread_mutex_t* mutex);使用该函数使得等待条件变量为真。线程被条件变量cond阻塞。

      int pthread_cond_timedwait(pthread_cond_t* cond, pthread_mutex_t* mutex,const struct timespec* tspr);与wait类似,只是经历tspr时间后,即使条件变量不满足,阻塞也被解除,返回错误码。

      int pthread_cond_signal(pthread_cond_t* cond);唤醒因为条件变量阻塞的线程。

      int pthread_cond_broadcast(pthread_cond_t* cond);唤醒等待该条件的所有线程。

      

    pthread_cond_t cond;
    pthread_mutex_t mutex;
    int count=0;
    void pthread1(void* arg){
        pthread_mutex_lock(&mutex);
        while(count==0)
            pthread_cond_wait(&cond,&mutex);
        count--;
        pthread_mutex_unlock(&mutex);
    }
    void pthread2(void* arg){
        pthread_mutex_lock(&mutex);
        if(count==0)
            pthread_cond_signal(&cond);
        count++;
        pthread_mutex_unlock(&mutex);
    }
    • 自旋锁

      互斥量阻塞线程的方式是使其进入睡眠,而自旋锁是让线程忙等,即不会使其睡眠,而是不断循判断自旋锁已经被解锁。

      适用于占用自旋锁时间比较短的情况。

    • 信号量

    介绍一下POSIX(POSIX标准定义了操作系统应该为应用程序提供的接口标准,换句话说,为一个POSIX兼容的操作系统编写的程序,应该可以在任何其它的POSIX操作系统(即使是来自另一个厂商)上编译执行。)的信号量机制,定义在头文件/usr/include/semaphore.h

    1)初始化一个信号量:sem_init()

    int sem_init(sem_t* sem,int pshared,unsigned int value);

    pshared为0时表示该信号量只能在当前进程的线程间共享,否则可以进程间共享,value给出了信号量的初始值。

    2)阻塞线程

    sem_wait(sem_t* sem)直到信号量sem的值大于0,解除阻塞后将sem的值减一,表明公共资源经使用后减少;sem_trywait(sem_t* sem)是wait的非阻塞版本,它直接将sem的值减一,相当于P操作。

    3)增加信号量的值,唤醒线程

    sem_post(sem_t* sem)会使已经被阻塞的线程其中的一个线程不再阻塞,选择机制同样是由线程的调度策略决定的。相当于V操作。

    3)释放信号量资源

    sem_destroy(sem_t* sem)用来释放信号量sem所占有的资源

    pthread_mutex_t mutex;
    sem_t full,empty;
    
    void producer(void* arg){
        while(1){
        sem_wait(&empty);//need to produce. the the empty of resource need minus 1
        pthread_mutex_lock(&mutex);
        ...//produce a resource
        pthread_mutex_unlock(&mutex);
        sem_post(&full); //have produced a resource, the the full of resource need add 1
        }
    }
    void consumer(void* arg){
        while(1){
        sem_wait(&full);
        pthread_mutex_lock(&mutex);
        ...//consume a resource
        pthread_mutex_unlock(&mutex);
        sem_post(&empty); 
        }
    }
  • 相关阅读:
    angularjs自定义指令complie和link属性
    港航环境变化引起的错误解决方法
    myBatis + SpringMVC上传、下载文件
    mybatis动态sql中的trim标签的使用
    MyBatis一对多和多对一
    常用的MIME类型
    SpringMVC 文件上传配置,多文件上传,使用的MultipartFile
    web自动化测试(7)--js操作
    web自动化测试(6)--下拉列表操作
    web自动化测试(5)--鼠标、键盘操作
  • 原文地址:https://www.cnblogs.com/LUO77/p/5754633.html
Copyright © 2011-2022 走看看