zoukankan      html  css  js  c++  java
  • 多线程专题之线程死锁原因之谜

    引子:线程死锁曾是多少程序员的噩梦,每每为此食不甘味,夜不成寐,一句话:苦不堪言。本文从几个场景入手,试图解开产生死锁的原因之谜。

    教科书:说的很具体,理解很抽象

      关于死锁产生的原因《操作系统》中有比较好的说明:

      (1)因为系统资源不足。

      (2)进程运行推进的顺序不合适。

      (3)资源分配不当等。

      关于死锁出现的必要条件也有比较具体的说明:

      (1)互斥条件:一个资源每次只能被一个进程使用。

      (2)请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。

      (3)不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。

      (4)循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。

      这四个条件是死锁的必要条件,只要系统发生死锁,这些条件必然成立,这也为我们实际应用中定位死锁问题,提供了路由。

    情景一、不加锁,两线程访问,变量访问示例

      关于死锁,有锁才能死,如果我们不加锁,自然不会发生死锁,但是如果不加锁,对资源的访问,将会发生什么情况呢。不妨看下面的例子:

      当两个线程读写相同变量时,线程A读取变量然后给予变量赋予一个新的值,但是写操作需要两个存储器周期。当线程B在这两个存储器周期中间读取这个相同变量时,它就会得到不一致的值。这就是为什么要对多线程资源访问进行加锁,加锁以后的访问顺序就变成了顺序访问,从而可以避免资源的不一致访问。

    情景二、不加锁,多线程访问,增量操作示例

      当两个或多个线程试图在同一时间修改同一个变量时,如果不加锁也会出现数据资源不一致的情况。如下图所示:

      我们可以看到,增量操作分为三个步骤进行:(1)从内存单元读入寄存器。(2)从寄存器中进行变量值的增加。(3)把新的值写回内存单元。如果两个线程试图同时对统一变量执行增量操作时,结果可能出现不一致。变量可能比原来增加了1,也可能增加了2,具体是1,还是2取决于第二个线程读取变量时获得的值是5还是6。这里面有一个前提就是变量增加的操作不是原子操作,这是因为现代计算机系统中,存储器访问需要多个总线周期,多处理器的总线周期通常在多个处理器上是交叉的,所以无法保证数据时顺序一致的。

    情景三、互斥锁,多变量部分锁

      以上示例已经讲明了我们为何需要线程锁,不加锁将会导致数据资源访问的不一致。可是加锁后,如果存在满足死锁的必要条件,又会产生死锁,我们该怎么办呢?不妨先来看一个示例:

      1 #include <stdlib.h>
      2 #include <pthread.h>
      3 
      4 #define NHASH 29
      5 #define HASH(fp) (((unsigned long)fp)%NHASH)
      6 
      7 struct foo *fh[NHASH];
      8 
      9 pthread_mutex_t hashlock = PTHREAD_MUTEX_INITIALIZER;
     10 
     11 struct foo {
     12     int             f_count;
     13     pthread_mutex_t f_lock;
     14     struct foo     *f_next; /* protected by hashlock */
     15     int             f_id;
     16     /* ... more stuff here ... */
     17 };
     18 
     19 struct foo *
     20 foo_alloc(void) /* allocate the object */
     21 {
     22     struct foo    *fp;
     23     int            idx;
     24 
     25     if ((fp = malloc(sizeof(struct foo))) != NULL) {
     26         fp->f_count = 1;
     27         if (pthread_mutex_init(&fp->f_lock, NULL) != 0) {
     28             free(fp);
     29             return(NULL);
     30         }
     31         idx = HASH(fp);
     32         pthread_mutex_lock(&hashlock);
     33         fp->f_next = fh[idx];
     34         fh[idx] = fp->f_next;
     35         pthread_mutex_lock(&fp->f_lock);
     36         pthread_mutex_unlock(&hashlock);
     37         /* ... continue initialization ... */
     38         pthread_mutex_unlock(&fp->f_lock);
     39     }
     40     return(fp);
     41 }
     42 //增加
     43 void
     44 foo_hold(struct foo *fp) /* add a reference to the object */
     45 {
     46     pthread_mutex_lock(&fp->f_lock);
     47     fp->f_count++;
     48     pthread_mutex_unlock(&fp->f_lock);
     49 }
     50 //查找已经对象
     51 struct foo *
     52 foo_find(int id) /* find an existing object */
     53 {
     54     struct foo    *fp;
     55     int            idx;
     56 
     57     idx = HASH(fp);
     58     pthread_mutex_lock(&hashlock);
     59     for (fp = fh[idx]; fp != NULL; fp = fp->f_next) {
     60         if (fp->f_id == id) {
     61             foo_hold(fp);
     62             break;
     63         }
     64     }
     65     pthread_mutex_unlock(&hashlock);
     66     return(fp);
     67 }
     68 //减小
     69 void
     70 foo_rele(struct foo *fp) /* release a reference to the object */
     71 {
     72     struct foo    *tfp;
     73     int            idx;
     74 
     75     pthread_mutex_lock(&fp->f_lock);
     76     if (fp->f_count == 1) { /* last reference */
     77         pthread_mutex_unlock(&fp->f_lock);  //如果不解锁会怎么样呢?
     78         pthread_mutex_lock(&hashlock);    //如果顺序发生变化呢?
     79         pthread_mutex_lock(&fp->f_lock);
     80         /* need to recheck the condition */
     81         if (fp->f_count != 1) {
     82             fp->f_count--;
     83             pthread_mutex_unlock(&fp->f_lock);
     84             pthread_mutex_unlock(&hashlock);
     85             return;
     86         }
     87         /* remove from list */
     88         idx = HASH(fp);
     89         tfp = fh[idx];
     90         if (tfp == fp) {
     91             fh[idx] = fp->f_next;
     92         } else {
     93             while (tfp->f_next != fp)
     94                 tfp = tfp->f_next;
     95             tfp->f_next = fp->f_next;
     96         }
     97         pthread_mutex_unlock(&hashlock);
     98         pthread_mutex_unlock(&fp->f_lock);
     99         pthread_mutex_destroy(&fp->f_lock);
    100         free(fp);
    101     } else {
    102         fp->f_count--;
    103         pthread_mutex_unlock(&fp->f_lock);
    104     }
    105 }

    以上代码注意加锁的顺序,如果顺序错了,则会有可能出现死锁。

  • 相关阅读:
    【流量劫持】SSLStrip 终极版 —— location 瞒天过海
    【流量劫持】沉默中的狂怒 —— Cookie 大喷发
    【流量劫持】SSLStrip 的未来 —— HTTPS 前端劫持
    Web 前端攻防(2014版)
    流量劫持 —— 浮层登录框的隐患
    流量劫持能有多大危害?
    流量劫持是如何产生的?
    XSS 前端防火墙 —— 整装待发
    XSS 前端防火墙 —— 天衣无缝的防护
    XSS 前端防火墙 —— 无懈可击的钩子
  • 原文地址:https://www.cnblogs.com/hadoopdev/p/3247406.html
Copyright © 2011-2022 走看看