互斥锁基本原理
互斥以排他方式防止共享数据被并发修改。互斥锁是一个二元变量,其状态为开锁(允许0)和上锁(禁止1),将某个共享资源与某个特定互斥锁绑定后,对该共享资源的访问如下操作:
(1)在访问该资源前,首先申请该互斥锁,如果该互斥处于开锁状态,则申请到该锁对象,并立即占有该锁(使该锁处于锁定状态),以防止其它线程访问该资源;如果该互斥锁处于锁定状态,默认阻塞等待;
(2)只有锁定该互斥锁的进程才能释放该互斥锁。其它线程的释放操作无效。
互斥锁基本操作函数
1. 初始化互斥锁
动态初始化
静态初始化
2. 申请互斥锁
3. 释放互斥锁
4. 销毁互斥锁
- extern int pthread_mutex_destroy(pthtread_mutex_t *_mutex)
5. 互斥锁应用实例
- #include <stdio.h>
- #include <unistd.h>
- #include <stdlib.h>
- #include <pthread.h>
- #include <semaphore.h>
- #include <string.h>
- void *thread_function(void *arg);
- pthread_mutex_t work_mutex;
- #define WORK_SIZE 1024
- char work_area[WORK_SIZE];
- int time_to_exit = 0;
- int main(int argc,char *argv[])
- {
- int res;
- pthread_t a_thread;
- void *thread_result;
- res = pthread_mutex_init(&work_mutex, NULL); //init mutex
- if (res != 0)
- {
- perror("Mutex initialization failed");
- exit(EXIT_FAILURE);
- }
- res = pthread_create(&a_thread, NULL, thread_function, NULL);//create new thread
- if (res != 0)
- {
- perror("Thread creation failed");
- exit(EXIT_FAILURE);
- }
- pthread_mutex_lock(&work_mutex); //lock the mutex
- printf("Input some text. Enter 'end' to finish\n");
- while(!time_to_exit)
- {
- fgets(work_area, WORK_SIZE, stdin); //get a string from stdin
- pthread_mutex_unlock(&work_mutex); //unlock the mutex
- while(1)
- {
- pthread_mutex_lock(&work_mutex); //lock the mutex
- if (work_area[0] != '\0')
- {
- pthread_mutex_unlock(&work_mutex); //unlock the mutex
- sleep(1);
- }
- else
- {
- break;
- }
- }
- }
- pthread_mutex_unlock(&work_mutex);
- printf("\nWaiting for thread to finish...\n");
- res = pthread_join(a_thread, &thread_result);
- if (res != 0)
- {
- perror("Thread join failed");
- exit(EXIT_FAILURE);
- }
- printf("Thread joined\n");
- pthread_mutex_destroy(&work_mutex);
- exit(EXIT_SUCCESS);
- }
- void *thread_function(void *arg)
- {
- sleep(1);
- pthread_mutex_lock(&work_mutex);
- while(strncmp("end", work_area, 3) != 0)
- {
- printf("You input %d characters\n", strlen(work_area) -1);
- printf("the characters is %s",work_area);
- work_area[0] = '\0';
- pthread_mutex_unlock(&work_mutex);
- sleep(1);
- pthread_mutex_lock(&work_mutex);
- while (work_area[0] == '\0' )
- {
- pthread_mutex_unlock(&work_mutex);
- sleep(1);
- pthread_mutex_lock(&work_mutex);
- }
- }
- time_to_exit = 1;
- work_area[0] = '\0';
- pthread_mutex_unlock(&work_mutex);
- pthread_exit(0);
- }
- $ ./mutex_example
- Input some text. Enter 'end' to finish
- hello mutex
- You input 11 characters
- the characters is hello mutex
- this is a sample
- You input 16 characters
- the characters is this is a sample
- end
- Waiting for thread to finish...
- 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