zoukankan      html  css  js  c++  java
  • 利用信号量避免死锁

    线程同步——互斥量     我们可以通过pthread提供的互斥量接口来保护我们的数据,确保每次只有一个线程访问。从本质上说就是一把锁,我们在访问共享数据的时候设置 (上锁),在访问完成后释放(解锁)。当我们解锁互斥量的时候,如果有多余一个的线程被阻塞,则所有阻塞在这个锁的进程都被唤醒,变成可以运行的状态。接来,只有一个线程开始运行并设置锁,其他的看到互斥量仍然是被锁定,继续等待。



    1. 初始化:

        线程的互斥量数据类型是pthread_mutex_t. 在使用前, 要对它进行初始化:

        对于静态分配的互斥量, 可以初始化为PTHREAD_MUTEX_INITIALIZER(等价于 pthread_mutex_init(…, NULL))或调用pthread_mutex_init。

        对于动态分配的互斥量, 在申请内存(malloc)之后, 通过pthread_mutex_init进行初始化, 并且在释放内存(free)前需要调用pthread_mutex_destroy.



    原型:

    int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restric attr);

    int pthread_mutex_destroy(pthread_mutex_t *mutex);

    返回值:成功则返回0, 出错则返回错误编号.

    说明:1、如果使用默认的属性初始化互斥量, 只需把attr设为NULL。

          2、销毁一个互斥锁即意味着释放它所占用的资源,且要求锁当前处于开放状态。由于在Linux中,互斥锁并不占用任何资源,因此LinuxThreads中的pthread_mutex_destroy()除了检查锁状态以外(锁定状态则返回EBUSY)没有其他动作。



    2. 互斥操作:

        对共享资源的访问, 要对互斥量进行加锁, 如果互斥量已经上了锁, 调用线程会阻塞, 直到互斥量被解锁。在完成了对共享资源的访问后, 要对互斥量进行解锁。

    首先说一下加锁函数:



    原型:

    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, 出错则返回错误编号.

    说明:1、想给一个互斥量上锁,我们调用pthread_mutex_lock。如果mutex已经上锁,调用的线程将会被阻塞,直至信号量解锁。

          2、具体说一下trylock函数, 这个函数是非阻塞调用模式, 也就是说, 如果互斥量没被锁住, trylock函数将把互斥量加锁, 并获得对共享资源的访问权限;如果互斥量被锁住了, trylock函数将不会阻塞等待而直接返回EBUSY, 表示共享资源处于忙状态。

          3、要解锁一个信号量,我们调用phtread_mutex_unlock。



    例子:

        我们使用mutex来保护数据结构:当多个进程需要访问动态申请的结构,我们嵌入了引用计数,来保证知道所有线程都使用完它时,我们才释放它。

    #include <pthread.h>  
    #include <stdlib.h>  

    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->f_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,则每个线程尝试锁定另一个互斥锁时,将会出现死锁。下面的例子说明了可能的死锁情况。

    线程 1
    线程 2
      
    pthread_mutex_lock(&m1);

    pthread_mutex_lock(&m2);

    pthread_mutex_unlock(&m2);

    pthread_mutex_unlock(&m1);
    pthread_mutex_lock(&m2);

    pthread_mutex_lock(&m1);

    pthread_mutex_unlock(&m1);

    pthread_mutex_unlock(&m2);
      

        避免此问题的最佳方法是,确保线程在锁定多个互斥锁时,以同样的顺序进行锁定。如果始终按照规定的顺序锁定,就不会出现死锁。此方法称为锁分层结构,它通过为互斥锁指定逻辑编号来对这些锁进行排序。

        另外,请注意以下限制:如果您持有的任何互斥锁其指定编号大于 n,则不能提取指定编号为 n 的互斥锁。

        但是,不能始终使用此方法。有时,必须按照与规定不同的顺序提取互斥锁。要防止在这种情况下出现死锁,请使用 pthread_mutex_trylock()。如果线程发现无法避免死锁时,该线程必须释放其互斥锁。

      

        总体来讲, 有几个不成文的基本原则:

        对共享资源操作前一定要获得锁。

        完成操作以后一定要释放锁。

        尽量短时间地占用锁。

        如果有多锁, 如获得顺序是ABC连环扣, 释放顺序也应该是ABC。

        线程错误返回时应该释放它所获得的锁。
  • 相关阅读:
    6:python2、python3 的区别及小数据池
    web前端----html表单操作
    web前端----html基础
    mysql数据库----索引原理与慢查询优化
    MySQL数据库----流程控制
    MySQL数据库----IDE工具介绍及数据备份
    MySQL数据库----数据锁
    MySQL数据库----事务处理
    MySQL数据库----事务
    MySQL数据库----函数
  • 原文地址:https://www.cnblogs.com/eagleking0318/p/6521184.html
Copyright © 2011-2022 走看看