Linux线程编程 - 线程同步机制之互斥锁

互斥锁基本原理

互斥以排他方式防止共享数据被并发修改。互斥锁是一个二元变量,其状态为开锁(允许0)和上锁(禁止1),将某个共享资源与某个特定互斥锁绑定后,对该共享资源的访问如下操作:
(1)在访问该资源前,首先申请该互斥锁,如果该互斥处于开锁状态,则申请到该锁对象,并立即占有该锁(使该锁处于锁定状态),以防止其它线程访问该资源;如果该互斥锁处于锁定状态,默认阻塞等待;
(2)只有锁定该互斥锁的进程才能释放该互斥锁。其它线程的释放操作无效。

互斥锁基本操作函数 


1. 初始化互斥锁

动态初始化


静态初始化


2. 申请互斥锁


3. 释放互斥锁


4. 销毁互斥锁

[cpp]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. extern int pthread_mutex_destroy(pthtread_mutex_t *_mutex)  

5. 互斥锁应用实例

[cpp]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. #include <stdio.h>  
  2. #include <unistd.h>  
  3. #include <stdlib.h>  
  4. #include <pthread.h>  
  5. #include <semaphore.h>  
  6. #include <string.h>  
  7.   
  8. void *thread_function(void *arg);  
  9.   
  10. pthread_mutex_t work_mutex;   
  11.   
  12. #define WORK_SIZE 1024  
  13. char work_area[WORK_SIZE];  
  14. int time_to_exit = 0;  
  15.   
  16. int main(int argc,char *argv[])   
  17. {  
  18.     int res;  
  19.     pthread_t a_thread;  
  20.     void *thread_result;  
  21.     res = pthread_mutex_init(&work_mutex, NULL); //init mutex   
  22.     if (res != 0)   
  23.     {  
  24.         perror("Mutex initialization failed");  
  25.         exit(EXIT_FAILURE);  
  26.     }  
  27.     res = pthread_create(&a_thread, NULL, thread_function, NULL);//create new thread  
  28.     if (res != 0)   
  29.     {  
  30.         perror("Thread creation failed");  
  31.         exit(EXIT_FAILURE);  
  32.     }  
  33.     pthread_mutex_lock(&work_mutex);            //lock the mutex  
  34.     printf("Input some text. Enter 'end' to finish\n");  
  35.     while(!time_to_exit)   
  36.     {  
  37.         fgets(work_area, WORK_SIZE, stdin);     //get a string from stdin  
  38.         pthread_mutex_unlock(&work_mutex);      //unlock the mutex  
  39.         while(1)   
  40.        {  
  41.             pthread_mutex_lock(&work_mutex);    //lock the mutex  
  42.             if (work_area[0] != '\0')   
  43.             {  
  44.                 pthread_mutex_unlock(&work_mutex);  //unlock the mutex  
  45.                 sleep(1);  
  46.             }  
  47.             else   
  48.             {  
  49.                 break;  
  50.             }  
  51.         }  
  52.     }  
  53.     pthread_mutex_unlock(&work_mutex);  
  54.     printf("\nWaiting for thread to finish...\n");  
  55.     res = pthread_join(a_thread, &thread_result);  
  56.     if (res != 0)   
  57.    {  
  58.         perror("Thread join failed");  
  59.         exit(EXIT_FAILURE);  
  60.     }  
  61.     printf("Thread joined\n");  
  62.     pthread_mutex_destroy(&work_mutex);  
  63.     exit(EXIT_SUCCESS);  
  64. }  
  65.   
  66. void *thread_function(void *arg)   
  67. {  
  68.     sleep(1);  
  69.     pthread_mutex_lock(&work_mutex);  
  70.     while(strncmp("end", work_area, 3) != 0)   
  71.     {  
  72.         printf("You input %d characters\n", strlen(work_area) -1);  
  73.         printf("the characters is %s",work_area);  
  74.         work_area[0] = '\0';  
  75.         pthread_mutex_unlock(&work_mutex);  
  76.         sleep(1);  
  77.         pthread_mutex_lock(&work_mutex);  
  78.         while (work_area[0] == '\0' )   
  79.         {  
  80.             pthread_mutex_unlock(&work_mutex);  
  81.             sleep(1);  
  82.             pthread_mutex_lock(&work_mutex);  
  83.         }  
  84.     }  
  85.     time_to_exit = 1;  
  86.     work_area[0] = '\0';  
  87.     pthread_mutex_unlock(&work_mutex);  
  88.     pthread_exit(0);  
  89. }  
运行结果:

[cpp]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. $ ./mutex_example   
  2. Input some text. Enter 'end' to finish  
  3. hello mutex  
  4. You input 11 characters  
  5. the characters is hello mutex  
  6. this is a sample  
  7. You input 16 characters  
  8. the characters is this is a sample  
  9. end  
  10.   
  11. Waiting for thread to finish...  
  12. Thread joined  


互斥锁使用分析

首先要弄清楚,线程互斥锁的作用是要保护共享资源在同一时刻只能被同一线程操作,即保证某一线程在上锁到解锁这一过程中对共享资源的操作为原子的。

现举例说明: 
设有两个线程:线程A和线程B(线程A和B在程序运行过程中被调度的顺序和时间不确定)。

全局变量(即线程A和B共享的资源)

<code class="hljs fix has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-attribute" style="box-sizing: border-box;">int i </span>=<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;"> 1;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;</span></code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li></ul>

为了彻底理解,分三种情况说明互斥锁的作用。

情形一:线程A和B均不使用互斥锁

<code class="hljs perl has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (i != <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>)
{
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">sleep</span>(<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1</span>);
    i = i - <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1</span>;
}
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">else</span>
{
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">printf</span>(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"i = 0\n"</span>);
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">exit</span>();
}</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li></ul>

分析: 
假如线程A正在执行语句if (i != 0),但距离执行i = i - 1;尚有一段时间间隔(为了便于理解间隔,让其休息1秒钟),恰好在这个间隔中线程B已经执行到了i = i - 1;可以知道,在A对i作判断时i 确实为1,但在B执行i = i - 1;之后,i已经变成0,此时A依然把i当成1来用,问题就在这里。问题可以描述为:在有两个线程对同一共享资源操作时,由于系统对线程的调度顺序存在不确定性,如果不对共享资源加以保护,则线程B极有可能在线程A不知道的情况下已经对共享资源进行了修改。这可不是我们想要的结果。

情形二:线程A和B都使用互斥锁(正确的方式)

<code class="hljs perl has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;">pthread_mutex_lock(mutex); 
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (i != <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>)
{
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">sleep</span>(<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1</span>);
    i = i - <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1</span>;
}
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">else</span>
{
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">printf</span>(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"i = 0\n"</span>);
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">exit</span>();
}
pthread_mutex_unlock(mutex);         </code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li></ul>

分析: 
假如线程A已经获得了锁,但是尚处于没有解锁的过程中,此时如果线程B**想要先获得锁然后再对I进行操作,则会阻塞在语句pthread_mutex_lock(mutex); 处,直到线程A释放锁之后线程B才有可能获得锁**。即,同一时刻,在互斥锁的保护下,同一共享资源只能被获得锁的线程操作。这才是我们想要的结果。

情形三:线程A使用互斥锁操作i,线程B不使用锁直接去操作i

<code class="hljs perl has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;">pthread_mutex_lock(mutex);           
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (i != <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>)
{
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">sleep</span>(<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1</span>);
    i = i - <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1</span>;
}
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">else</span>
{
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">printf</span>(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"i = 0\n"</span>);
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">exit</span>();
}
pthread_mutex_unlock(mutex);       </code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li></ul>

分析: 
假如线程A已经获得了锁,但是尚处于没有解锁的过程中,此时线程B却直接对i进行操作却不进行加锁操作。那么,线程B能不能对i进行操作呢?答案是肯定的。只不过,这属于代码设计缺陷,因为,线程A之所以上锁之后再去操作i目的就是要实现对i的操作是原子的,而线程B比较粗鲁,直接对i操作,结果就变成了情形一。即,互斥锁已然成了摆设,没能实现其价值。显然,这也不是我们想要的结果。

总结:

通过上面三种情形的分析,可以知道互斥锁可以保护共享资源免于被同时操作,不过仅仅是可以。因为情形三已经说明了问题,线程B可以直接对i操作,但这种情况下互斥锁已经没有了意义。 
因此,如果想使用互斥锁来实现线程对共享资源的原子操作,则任何一个线程在想要对共享资源操作前,都要先去获得锁,在操作完之后再释放锁,如此方为正道!



原文链接:

http://blog.csdn.net/geng823/article/details/41344735


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值