Linux多线程——使用互斥量同步线程

前文再续,书接上一回,在上一篇文章:Linux多线程——使用信号量同步线程中,我们留下了一个如何使用互斥量来进行线程同步的问题,本文将会给出互斥量的详细解说,并用一个互斥量解决上一篇文章中,要使用两个信号量才能解决的只有子线程结束了对输入的处理和统计后,主线程才能继续执行的问题。

一、什么是互斥量

互斥量是另一种用于多线程中的同步访问方法,它允许程序锁住某个对象,使得每次只能有一个线程访问它。为了控制对关键代码的访问,必须在进入这段代码之前锁住一个互斥量,然后在完成操作之后解锁。

二、互斥量的函数的使用

它们的定义与使用信号量的函数非常相似,它们的定义如下:
[cpp] view plaincopyprint?
  1. #include <pthread.h>  
  2. int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr);  
  3.   
  4. int pthread_mutex_lock(pthread_mutex_t *mutex);  
  5.   
  6. int pthread_mutex_unlock(pthread_mutex_t *mutex);  
  7.   
  8. 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.   
  8. //声明线程函数和互斥量  
  9. void* thread_func(void *msg);  
  10. pthread_mutex_t mutex;  
  11.   
  12.   
  13. #define MSG_SIZE 512  
  14.   
  15.   
  16. int main()  
  17. {  
  18.     int res = -1;  
  19.     pthread_t thread;  
  20.     void *thread_result = NULL;  
  21.     char msg[MSG_SIZE] = {'\0'};  
  22.     //初始化互斥量,使用默认的互斥量属性  
  23.     res = pthread_mutex_init(&mutex, NULL);  
  24.     if(res != 0)  
  25.     {  
  26.         perror("pthread_mutex_init failed\n");  
  27.         exit(EXIT_FAILURE);  
  28.     }  
  29.     //创建子线程,并把msg作为线程函数的参数传递给thread_func  
  30.     res = pthread_create(&thread, NULL, thread_func, msg);  
  31.     if(res != 0)  
  32.     {  
  33.         perror("pthread_create failed\n");  
  34.         exit(EXIT_FAILURE);  
  35.     }  
  36.     //输入字符串,以串‘end’结束  
  37.     printf("Input some test. Enter 'end' to finish\n");  
  38.     //把互斥量mutex加锁,以确保同一时间只有该线程可以访问msg中的数据  
  39.     pthread_mutex_lock(&mutex);  
  40.     while(strcmp("end\n", msg) != 0)  
  41.     {  
  42.         if(strncmp("TEST", msg, 4) == 0)  
  43.         {  
  44.             strcpy(msg, "copy_data\n");  
  45.         }  
  46.         else  
  47.         {  
  48.             fgets(msg, MSG_SIZE, stdin);  
  49.         }  
  50.         //把互斥量mutex解锁,让其他的线程可以访问msg中的数据  
  51.         pthread_mutex_unlock(&mutex);  
  52.         sleep(1);//休眠1秒再继续循环,让其他线程有执行的机会  
  53.         pthread_mutex_lock(&mutex);  
  54.     }  
  55.     pthread_mutex_unlock(&mutex);  
  56.     printf("\nWaiting for thread finish...\n");  
  57.     //等待子线程结束  
  58.     res = pthread_join(thread, &thread_result);  
  59.     if(res != 0)  
  60.     {  
  61.         perror("pthread_join failed\n");  
  62.         exit(EXIT_FAILURE);  
  63.     }  
  64.     printf("Thread joined\n");  
  65.     //清理互斥量  
  66.     pthread_mutex_destroy(&mutex);  
  67.     exit(EXIT_SUCCESS);  
  68. }  
  69. void* thread_func(void *msg)  
  70. {  
  71.     int i = 0;  
  72.     char *ptr = msg;  
  73.     sleep(1);  
  74.     //把互斥量mutex加锁,以确保同一时间只有该线程可以访问msg中的数据  
  75.     pthread_mutex_lock(&mutex);  
  76.     while(strcmp("end\n", msg) != 0)  
  77.     {  
  78.         //把小写字母变成大写  
  79.         for(i = 0; ptr[i] != '\0'; ++i)  
  80.         {  
  81.             if(ptr[i] >= 'a' && ptr[i] <='z')  
  82.             {  
  83.                 ptr[i] -= 'a' - 'A';  
  84.             }  
  85.         }  
  86.         printf("You input %d characters\n", i-1);  
  87.         printf("To uppercase: %s\n", ptr);  
  88.         //把互斥量mutex解锁,让其他的线程可以访问msg中的数据  
  89.         pthread_mutex_unlock(&mutex);  
  90.         sleep(1);//休眠1秒再继续循环,让其他线程有执行的机会  
  91.         pthread_mutex_lock(&mutex);  
  92.     }  
  93.     pthread_mutex_unlock(&mutex);  
  94.     //退出线程  
  95.     pthread_exit(NULL);  
  96. }  
运行结果如下:


程序分析

这个程序的工作流程已经说得非常清楚了,这里先来说说在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$ 



本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/469724.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

asp.net基础知识

面向对象编程特征&#xff1a;抽象化&#xff0c;讲究对象&#xff0c;集成&#xff0c;封装&#xff0c;多态。 类&#xff1a;一类事物&#xff0c;苹果&#xff0c;梨&#xff0c;香蕉都是水果&#xff0c;都是一类。 类的特征&#xff1a;构造函数&#xff0c;字段属性和方…

智慧交通day02-车流量检测实现05:小车匀速案例

""" 现在利用卡尔曼滤波对小车的运动状态进行预测。主要流程如下所示&#xff1a;导入相应的工具包小车运动数据生成参数初始化利用卡尔曼滤波进行小车状态预测可视化&#xff1a;观察参数的变化与结果 """#导入包 from matplotlib import pyplo…

ubuntu start

首先用你当初装ubuntu的方法进入live cd打开终端 输入以下命令&#xff1a;1&#xff1a;sudo -i2&#xff1a;fdisk -l &#xff08;找一找你的ubuntu安装在那个分区&#xff09;下面挂载原系统的根目录&#xff1a;mount /dev/sdax /mnt&#xff08;x代表你Ubuntu所在分区编号…

排座椅

题目描述 上课的时候总会有一些同学和前后左右的人交头接耳&#xff0c;这是令小学班主任十分头疼的一件事情。不过&#xff0c;班主任小雪发现了一些有趣的现象&#xff0c;当同学们的座次确定下来之后&#xff0c;只有有限的D对同学上课时会交头接耳。同学们在教室中坐成了M行…

智慧交通day02-车流量检测实现05:小车匀加速案例

""" 现在利用卡尔曼滤波对小车的运动状态进行预测。主要流程如下所示&#xff1a;导入相应的工具包小车运动数据生成参数初始化利用卡尔曼滤波进行小车状态预测可视化&#xff1a;观察参数的变化与结果 """#导入包 from matplotlib import pyplo…

无法连接上 cn.archive.ubuntu.com:80 (123.129.214.98)。 - connect (111: 拒绝连接)

今天用sudo apt-get install flex的时候出现了标题上的问题&#xff0c;不能连接。 先ping了一下这个域名&#xff0c;发现能够ping的通&#xff0c;就是不懂为什么不能链接。 接着从网上找相应的解决方法&#xff0c;有网友说是源出问题拉&#xff0c;权且认为是这个源的主机…

智慧交通day02-车流量检测实现06:目标估计模型-卡尔曼滤波

在这里我们主要完成卡尔曼滤波器进行跟踪的相关内容的实现。 初始化&#xff1a;卡尔曼滤波器的状态变量和观测输入更新状态变量根据状态变量预测目标的边界框初始化&#xff1a; 状态量x的设定是一个七维向量&#xff1a; 分别表示目标中心位置的x,y坐标&#xff0c;面积s和当…

在Ubuntu下安装jdk解压出现问题:./jdk-6u30-linux-i586.bin: 113: ./install.sfx.3631: not found

在Ubuntu下安装jdk解压出现问题&#xff1a;./jdk-6u30-linux-i586.bin: 113: ./install.sfx.3631: not found。 Failed to extract the files. Please refer to the Troubleshooting section of the Installation Instructions on the download page for more information 网…

Elastci LogStash

运行: cmd -f ../conf转载于:https://www.cnblogs.com/grayguo/p/6160953.html

python或anaconda下安装opencv提示Error:No matching distribution found for opencv

python或anaconda下安装opencv提示Error&#xff1a;No matching distribution found for opencv 错误提示&#xff1a; ERROR: Could not find a version that satisfies the requirement python-opencv (from versions: none) ERROR: No matching distribution found for p…

ubuntu下安装jdk1.6.0_41

1、从网上下载jdk。看好自己的系统是32位还是64位的。用uname -m来查看。 http://www.oracle.com/technetwork/java/javasebusiness/downloads/java-archive-downloads-javase6-419409.html#jre-6u43-oth-JPR 2、运行命令如下。 sudo chmod 777 jdk-6u41-linux-x64.bin sud…

iOS 10 的坑:新机首次安装 app,请求网络权限“是否允许使用数据”(转)

转载自&#xff1a;文&#xff0f;戴仓薯&#xff08;简书作者&#xff09;原文链接&#xff1a;http://www.jianshu.com/p/6cbde1b8b922症状 iOS 10 之后&#xff0c;陆陆续续地有用户联系我们&#xff0c;说新机第一次安装、第一次启动的时候&#xff0c;app 首屏一片空白&am…

智慧交通day02-车流量检测实现06:目标估计模型-卡尔曼滤波(汇总)

from __future__ import print_function from numba import jit import numpy as np from scipy.optimize import linear_sum_assignment from filterpy.kalman import KalmanFilter#计算IOU&#xff08;交并比&#xff09; jit def iou(bb_test,bb_gt):"""在两…

tensorflow训练神经网络时loss出现nan的问题

tensorflow训练神经网络时loss出现nan的问题 一般情况下原因是由于优化器上的学习比率learning_rate定义值太大&#xff0c;如&#xff1a; train_step tf.compat.v1.train.GradientDescentOptimizer(0.1).minimize(loss) 运行打印loss&#xff1a; for i in range(100):se…

Ubuntu 软件包管理详解

Ubuntu 方便宜用&#xff0c;最值得让人称道的便是其安装软件的方式, 一条命令: sudo apt-get install xxx 就几乎能帮你搞定所有的软件安装难题。但是有时你可能有这样的需求&#xff0c;查看某个软件包是否安装、安装在哪..., 那我们就来介绍一下 Ubuntu 的软件包管理方式。 …

Redis入门指南(第2版) Redis设计思路学习与总结

https://www.qcloud.com/community/article/222 宋增宽&#xff0c;腾讯工程师&#xff0c;16年毕业加入腾讯&#xff0c;从事海量服务后台设计与研发工作&#xff0c;现在负责QQ群后台等项目&#xff0c;喜欢研究技术&#xff0c;并思考技术演变&#xff0c;专注于高并发业务架…

智慧交通day02-车流量检测实现07:匈牙利算法

匈牙利算法&#xff08;Hungarian Algorithm&#xff09;与KM算法&#xff08;Kuhn-Munkres Algorithm&#xff09;是用来解决多目标跟踪中的数据关联问题&#xff0c;匈牙利算法与KM算法都是为了求解二分图的最大匹配问题。 有一种很特别的图&#xff0c;就做二分图&#xff0…

非线性回归(Non-linear Regression)学习笔记

非线性回归&#xff08;Non-linear Regression&#xff09; 1.概率: 1.1定义概率Probability:对一件事情发生的可能性的衡量 1.2范围 0<P<1 1.3计算方法: 1.3.1根据个人置信 1.3.2根据历史数据 1.3.3根据模拟数据 1.4条件概率:&#xff08;A发生的条件下B发生的概率&…

Linux 设备驱动模型中的class(类)

首先&#xff0c;想说明一下&#xff0c;促使我研究class&#xff08;类&#xff09;的是因为它能够自动创建/dev下的设备节点。当然class还有其另外的作用&#xff0c;且自动创建设备节点的还有udev系统&#xff0c;udev是处于用户空间的&#xff0c;其自动创建设备节点也是依…

智慧交通day02-车流量检测实现08:目标跟踪中的数据关联(将检测框bbox与卡尔曼滤波器的跟踪框进行关联匹配)

# 将YOLO模型的检测框和卡尔曼滤波的跟踪框进行匹配 def associate_detection_to_tracker(detections,trackers,iou_threshold0.3):"""将检测框bbox与卡尔曼滤波器的跟踪框进行关联匹配:param detections:检测框:param trackers:跟踪框&#xff0c;即跟踪目标:p…