前文再续,书接上一回,在上一篇文章:Linux多线程——使用信号量同步线程中,我们留下了一个如何使用互斥量来进行线程同步的问题,本文将会给出互斥量的详细解说,并用一个互斥量解决上一篇文章中,要使用两个信号量才能解决的只有子线程结束了对输入的处理和统计后,主线程才能继续执行的问题。
一、什么是互斥量
互斥量是另一种用于多线程中的同步访问方法,它允许程序锁住某个对象,使得每次只能有一个线程访问它。为了控制对关键代码的访问,必须在进入这段代码之前锁住一个互斥量,然后在完成操作之后解锁。
二、互斥量的函数的使用
它们的定义与使用信号量的函数非常相似,它们的定义如下:
[cpp] view plaincopyprint?
  1. #include <pthread.h>
  2. int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr);
  3. int pthread_mutex_lock(pthread_mutex_t *mutex);
  4. int pthread_mutex_unlock(pthread_mutex_t *mutex);
  5. int pthread_mutex_destroy(pthread_mutex_t *mutex);
它们的意义就如它们的名字所示的那样,成功时返回0,失败时返回错误代码,它们并不设置errno。
pthread_mutex_init函数中的参数mutexattr指定互斥量的属性,在这里我们并不关心互斥量的属性,所以把它设置为NULL,使用默认属性即可。同样的,pthread_mutex_lock和pthread_mutex_unlock都是原子操作,如果一个线程调用pthread_mutex_lock试图锁住互斥量,而该互斥量,又被其他线程锁住(占用),则该线程的pthread_mutex_lock调用就会阻塞,直到其他线程对该互斥量进行解锁,该线程才能获得该互斥量,pthread_mutex_lock调用才会返回。
注意,使用互斥量的默认属性,如果程序试图对一个已经加锁的互斥量调用pthread_mutex_lock,程序就会阻塞,而又因为拥有互斥量的这个线程正是现在被阻塞的线程,所以这个互斥量就永远不会被解锁,也就是说,程序就会进入死锁的状态。在使用时要多加注意,确保在同一个线程中,对加锁的互斥再次进行加锁前要对其进行解锁。
三、使用互斥量进行线程同步
下面以一个简单的多线程程序来演示如何使用互斥量来进行线程同步。在主线程中,我们创建子线程,并把数组msg作为参数传递给子线程,然后主线程调用函数pthread_mutex_lock对互斥量加锁,等待输入,输入完成后,调用函数pthread_mutex_unlock对互斥量解锁,从而使线程函数中的对互斥量加锁的pthread_mutex_lock函数返回并执行子线程中的代码。线程函数在把字符串的小写字母变成大写并统计输入的字符数量之后,它调用pthread_mutex_unlock对互斥量解锁,使主线程能够继续获得互斥量(即对其加锁函数返回),再次执行输入功能直到主线程再次调用pthread_mutex_unlock对其解锁,一直如此重复,直到输入end。
源文件为lockthread.c,源代码如下:
[cpp] view plaincopyprint?
  1. #include <unistd.h>
  2. #include <pthread.h>
  3. #include <stdlib.h>
  4. #include <stdio.h>
  5. #include <string.h>
  6. //声明线程函数和互斥量
  7. void* thread_func(void *msg);
  8. pthread_mutex_t mutex;
  9. #define MSG_SIZE 512
  10. int main()
  11. {
  12. int res = -1;
  13. pthread_t thread;
  14. void *thread_result = NULL;
  15. char msg[MSG_SIZE] = {'\0'};
  16. //初始化互斥量,使用默认的互斥量属性
  17. res = pthread_mutex_init(&mutex, NULL);
  18. if(res != 0)
  19. {
  20. perror("pthread_mutex_init failed\n");
  21. exit(EXIT_FAILURE);
  22. }
  23. //创建子线程,并把msg作为线程函数的参数传递给thread_func
  24. res = pthread_create(&thread, NULL, thread_func, msg);
  25. if(res != 0)
  26. {
  27. perror("pthread_create failed\n");
  28. exit(EXIT_FAILURE);
  29. }
  30. //输入字符串,以串‘end’结束
  31. printf("Input some test. Enter 'end' to finish\n");
  32. //把互斥量mutex加锁,以确保同一时间只有该线程可以访问msg中的数据
  33. pthread_mutex_lock(&mutex);
  34. while(strcmp("end\n", msg) != 0)
  35. {
  36. if(strncmp("TEST", msg, 4) == 0)
  37. {
  38. strcpy(msg, "copy_data\n");
  39. }
  40. else
  41. {
  42. fgets(msg, MSG_SIZE, stdin);
  43. }
  44. //把互斥量mutex解锁,让其他的线程可以访问msg中的数据
  45. pthread_mutex_unlock(&mutex);
  46. sleep(1);//休眠1秒再继续循环,让其他线程有执行的机会
  47. pthread_mutex_lock(&mutex);
  48. }
  49. pthread_mutex_unlock(&mutex);
  50. printf("\nWaiting for thread finish...\n");
  51. //等待子线程结束
  52. res = pthread_join(thread, &thread_result);
  53. if(res != 0)
  54. {
  55. perror("pthread_join failed\n");
  56. exit(EXIT_FAILURE);
  57. }
  58. printf("Thread joined\n");
  59. //清理互斥量
  60. pthread_mutex_destroy(&mutex);
  61. exit(EXIT_SUCCESS);
  62. }
  63. void* thread_func(void *msg)
  64. {
  65. int i = 0;
  66. char *ptr = msg;
  67. sleep(1);
  68. //把互斥量mutex加锁,以确保同一时间只有该线程可以访问msg中的数据
  69. pthread_mutex_lock(&mutex);
  70. while(strcmp("end\n", msg) != 0)
  71. {
  72. //把小写字母变成大写
  73. for(i = 0; ptr[i] != '\0'; ++i)
  74. {
  75. if(ptr[i] >= 'a' && ptr[i] <='z')
  76. {
  77. ptr[i] -= 'a' - 'A';
  78. }
  79. }
  80. printf("You input %d characters\n", i-1);
  81. printf("To uppercase: %s\n", ptr);
  82. //把互斥量mutex解锁,让其他的线程可以访问msg中的数据
  83. pthread_mutex_unlock(&mutex);
  84. sleep(1);//休眠1秒再继续循环,让其他线程有执行的机会
  85. pthread_mutex_lock(&mutex);
  86. }
  87. pthread_mutex_unlock(&mutex);
  88. //退出线程
  89. pthread_exit(NULL);
  90. }
运行结果如下:
程序分析:
这个程序的工作流程已经说得非常清楚了,这里先来说说在main函数和线程函数thread_func中while循环中的sleep(1)语句的作用。可能很多人会认为这个sleep(1)是为了让子线程完成其处理和统计功能,所以要让主线程休眠1秒钟来等待子线程的处理统计工作的完成。的确在这里子线程进行的工作十分简单,1秒钟内的确可以处理统计完毕。但是这里的sleep(1)并不是为了实现这个功能,这两个循环中的sleep(1)是为了让其他的线程有机会被执行到,如果在一次的加锁和解锁之间没有这条语句的话,则当前的线程将会一直在循环中获得互斥量,因为其他的线程没有执行它的代码的时间,所以就要用这样的一条语句来给其他的线程一个运行的机会。如果子线程的执行时间超过1秒,这个程序还是会正常运行。
以这个例子来说,在主线程中,当输入数据完毕并对互斥量解锁之后,并不马上循环对其加锁,此时子线程就有了执行的机会,它会对互斥量进行加锁,同样地,当它处理统计完输入的数据后,它在进入下一次循环前,也休眠1秒,让主线程有机会再次运行。而主线程什么时候能够执行,取决于子线程何时对互斥量进行解锁。因为如果子线程拥有(锁住)互斥量,则主线程中函数pthread_mutex_lock就不会返回,使主线程处于阻塞状态。
换句话来说,就是只有子线程结束了对输入的处理和统计后,主线程才能继续执行,向msg中写入数据。看到这里,你应该知道之前在使用信号量时,我们多用一个信号量也是为了达到这个目的。所以当我们输入TEST时,程序有两个输入,但还是能正常运行,同样解决了之前使用一个信号量时所带来的问题。
信号量和互斥量的作用都是保护代码段的互斥设备,它们也非常相似。但在本例中,与使用信号量相比,实现同样的功能,如果使用信号量的话,则需要两个信号量,而使用互斥量的话,只需要一个。可以说在本例中,使用互斥量更简单。但是我觉得使用互斥量更容易犯错,我们可以看到在这个例子中,我们需要使用sleep语句来让其他线程获得执行的机会,但是在使用信号量的程序,它并不需要使用sleep,相对来说比较直观。我知道可能是我的实现方法不好,但是对于使用互斥量来说,我想了很久也想不到不使用sleep的方法。
undefined reference to 'pthread_create'
undefined reference to 'pthread_join'
问题解决:
    在编译中要加 -lpthread参数
    gcc thread.c -o thread -lpthread
    thread.c为你些的源文件,不要忘了加上头文件#include<pthread.h>
我自己的试验例子
#include <unistd.h>
#include <pthread.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>//声明线程函数和互斥量
void* thread_func(void *msg);
pthread_mutex_t mutex;#define MSG_SIZE 512int main()
{int res = -1;pthread_t thread;void *thread_result = NULL;char msg[MSG_SIZE] = {'\0'};//初始化互斥量,使用默认的互斥量属性res = pthread_mutex_init(&mutex, NULL);if(res != 0){perror("pthread_mutex_init failed\n");exit(EXIT_FAILURE);}//创建子线程,并把msg作为线程函数的参数传递给thread_funcres = pthread_create(&thread, NULL, thread_func, msg);if(res != 0){perror("pthread_create failed\n");exit(EXIT_FAILURE);}//输入字符串,以串‘end’结束printf("Input some test. Enter 'end' to finish\n");//把互斥量mutex加锁,以确保同一时间只有该线程可以访问msg中的数据pthread_mutex_lock(&mutex);while(strcmp("end\n", msg) != 0){if(strncmp("TEST", msg, 4) == 0)/*输入的msg是TEST*/{printf("www=============================test1\n");strcpy(msg, "copy_data\n");}else/*输入的msg不是TEST*/{printf("www=============================test2\n");fgets(msg, MSG_SIZE, stdin);/*等待用户输入*/}//把互斥量mutex解锁,让其他的线程可以访问msg中的数据pthread_mutex_unlock(&mutex);sleep(1);//休眠1秒再继续循环,让其他线程有执行的机会pthread_mutex_lock(&mutex);}pthread_mutex_unlock(&mutex);printf("\nWaiting for thread finish...\n");//等待子线程结束res = pthread_join(thread, &thread_result);if(res != 0){perror("pthread_join failed\n");exit(EXIT_FAILURE);}printf("Thread joined\n");//清理互斥量pthread_mutex_destroy(&mutex);exit(EXIT_SUCCESS);
}
void* thread_func(void *msg)/*子线程的功能是把主线程的传下来的字母变成大写字母*/
{int i = 0;char *ptr = msg;sleep(1);//把互斥量mutex加锁,以确保同一时间只有该线程可以访问msg中的数据pthread_mutex_lock(&mutex);while(strcmp("end\n", msg) != 0){//把小写字母变成大写for(i = 0; ptr[i] != '\0'; ++i){if(ptr[i] >= 'a' && ptr[i] <='z'){ptr[i] -= 'a' - 'A';}}printf("You input %d characters\n", i-1);printf("To uppercase: %s\n", ptr);//把互斥量mutex解锁,让其他的线程可以访问msg中的数据pthread_mutex_unlock(&mutex);sleep(1);//休眠1秒再继续循环,让其他线程有执行的机会pthread_mutex_lock(&mutex);}pthread_mutex_unlock(&mutex);//退出线程pthread_exit(NULL);
}
wqf@wqf-System-Product-Name:/worksen/lddexamples/muax$ gcc  muax.c -o muax -lpthread
wqf@wqf-System-Product-Name:/worksen/lddexamples/muax$ ./muax
Input some test. Enter 'end' to finish
www=============================test2
qqq
You input 3 characters
To uppercase: QQQwww=============================test2
TEST
You input 4 characters
To uppercase: TESTwww=============================test1
You input 9 characters
To uppercase: COPY_DATAwww=============================test2
eee
You input 3 characters
To uppercase: EEEwww=============================test2
endWaiting for thread finish...
Thread joined
wqf@wqf-System-Product-Name:/worksen/lddexamples/muax$ 

Linux多线程——使用互斥量同步线程相关推荐

  1. linux 只运行一个实例 互斥锁,Linux多线程4-1_互斥量

    //包含头文件 int pthread_mutex_destroy(pthread_mutex_t *mutex); int pthread_mutex_init(pthread_mutex_t *r ...

  2. Visual C++利用互斥量同步线程实现文件读取进度条

    忘了原文的位置了. 一.前言 文件读取进度条的实现可以有很多种方法,常用的是在读取文件的过程中隔一定时间向对话框发送消息以控制进度条的位置,但是这种方法很难确定隔多少时问发送一个消息,因为文件的大小是 ...

  3. 多线程编程(3): 使用互斥锁同步线程

    python多线程编程(3): 使用互斥锁同步线程 问题的提出 上一节的例子中,每个线程互相独立,相互之间没有任何关系.现在假设这样一个例子:有一个全局的计数num,每个线程获取这个全局的计数,根据n ...

  4. linux多线程拷贝信号量运用于线程之间通讯遇到的printf问题

    linux多线程拷贝信号量运用于线程之间通讯遇到的printf问题 1.题目内容 使用多线程实现文件的拷贝,并使用进度条进行相应的显示 2.进度 以下代码实现了多线程完成文件的拷贝,但是在使用进度条实 ...

  5. python多线程编程(2): 使用互斥锁同步线程

    上一节的例子中,每个线程互相独立,相互之间没有任何关系.现在假设这样一个例子:有一个全局的计数num,每个线程获取这个全局的计数,根据num进行一些处理,然后将num加1.很容易写出这样的代码: # ...

  6. python 线程锁_python多线程编程(3): 使用互斥锁同步线程

    问题的提出 上一节的例子中,每个线程互相独立,相互之间没有任何关系.现在假设这样一个例子:有一个全局的计数num,每个线程获取这个全局的计数,根据num进行一些处理,然后将num加1.很容易写出这样的 ...

  7. python多线程编程(3): 使用互斥锁同步线程

    问题的提出 上一节的例子中,每个线程互相独立,相互之间没有任何关系.现在假设这样一个例子:有一个全局的计数num,每个线程获取这个全局的计数,根据num进行一些处理,然后将num加1.很容易写出这样的 ...

  8. python多线程同步与互斥_python多线程编程(3): 使用互斥锁同步线程

    问题的提出 上一节的例子中,每个线程互相独立,相互之间没有任何关系.现在假设这样一个例子:有一个全局的计数num,每个线程获取这个全局的计数,根据num进行一些处理,然后将num加1.很容易写出这样的 ...

  9. linux c进程线程的面试问题,linux 多线程面试题_linux进程线程_linux 线程 pthread_create...

    你写了一个简单的mandelbrot set程序,也就是说用ps命令行是可以看见多个线程,win32里同一个进程里各个线程之间是共享数据段的,win32的进程管理方式与unix上有着很大区别.adob ...

最新文章

  1. 当程序出Bug时,程序员最喜欢说的30句话
  2. OKR落地,实践经验总结两个点比较重要
  3. 标题h和img优化的技巧
  4. 关于sigma pix的理解
  5. OpenCV GPU 简单遍历图像
  6. ppt批量缩略图_PPT如何在文件夹下显示缩略图的方法
  7. 搭建webpack基础配置
  8. WSSv3 Technical Articles_Windows SharePoint Services 3.0编码开发工具和技巧(Part 2 of 2)
  9. pyspark对应的scala代码PythonRDD类
  10. React中useEffect使用
  11. git向远程推送代码提示fatal: Authentication failed
  12. Ubuntu部署KVM服务器
  13. 【干货摘录】和秋叶一起学PPT书本中干货集合、PPT设计必备网站、找字体网站
  14. JTAG接口定义与其他简介
  15. week15-字符串
  16. POJ 1129 Channel Allocation(四色定理)
  17. 移动魔百盒CM201-2,YS代工-免拆机-直刷固件及教程
  18. 技术管理规划-如何设定团队的目标
  19. 华为云添加HSS云主机agent无法安装提示已存在问题处理
  20. 高德的位置服务器,高德位置服务浅析

热门文章

  1. Oracle PL/SQL匿名块(三)
  2. php 逗号 分割字符串
  3. 教你如何防止电脑插入u盘后自动运行
  4. 使用pandas进行量化回测(akshare)
  5. 基于matlab的大米,大米颗数计算MATLAB软件
  6. python发展历程
  7. 华为2017java笔试题_2017年java华为面试题
  8. [c++]代理对象模式
  9. php实现服务器文件同步,PHPstorm配置同步服务器文件
  10. h5 android数字键盘,【笔记】移动端H5数字键盘input type=number的处理(IOS和Android)...