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,一经查实,立即删除!

相关文章

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

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

排座椅

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

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

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

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

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

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…

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):"""在两…

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发生的概率&…

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

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

回归中的相关度和R平方值 学习笔记

回归中的相关度和R平方值 自变量x和因变量y的相关度 1.皮尔逊相关系数(Pearson Correlation Coefficient): 1.1衡量两个值线性相关强度的量 1.2取值范围[-1,1]: 正向相关: >0,负向相关: <0,无相关性: 0 公式&#xff1a;correlation&#xff0c; correlationvariance(Co…

智慧交通day02-车流量检测实现09:SORT/deepSORT

SORT和DeepSORT是多目标跟踪中两个知名度比较高的算法。DeepSORT是原团队对SORT的改进版本。现在来解析一下SORT和DeepSORT的基本思路。 1.SORT SORT核心是卡尔曼滤波和匈牙利匹配两个算法。流程图如下所示&#xff0c;可以看到整体可以拆分为两个部分&#xff0c;分别是匹配…

素数环 与 算法 全排列

在说起全排列前&#xff0c;先说一下昨天碰到的一个题目&#xff08;答案不是我做出来的&#xff0c;但是我感觉有好多个亮点&#xff0c;贴出来方便日后的学习&#xff09;&#xff1a; 素数环 时间限制&#xff1a;1000 ms | 内存限制&#xff1a;65535 KB难度&#xff1a;…

简单线性回归(Simple Linear Regression)和多元线性回归(Multiple Regression)学习笔记

简单线性回归(Simple Linear Regression) 0.前提介绍: 为什么需要统计量? 统计量:描述数据特征 0.1集中趋势衡量 0.1.1均值(平均数&#xff0c;平均值) (mean)&#xff1a;&#xff08;求和除以个数&#xff0c;Ex也可以表示x求均值&#xff09; 0.1.2中位数(median) : 将数…

智慧交通day02-车流量检测实现10:多目标追踪实现

在这里我们主要实现了一个多目标跟踪器&#xff0c;管理多个卡尔曼滤波器对象&#xff0c;主要包括以下内容&#xff1a; 初始化&#xff1a;最大检测数&#xff0c;目标未被检测的最大帧数 目标跟踪结果的更新&#xff0c;即跟踪成功和失败的目标的更新 初始化 def __init_…

智慧交通day02-车流量检测实现11:yoloV3模型

yoloV3以V1&#xff0c;V2为基础进行的改进&#xff0c;主要有&#xff1a;利用多尺度特征进行目标检测&#xff1b;先验框更丰富&#xff1b;调整了网络结构&#xff1b;对象分类使用logistic代替了softmax,更适用于多标签分类任务。 1.算法简介 YOLOv3是YOLO (You Only Loo…

bzoj1992鬼谷子的钱袋(二分乱搞 二进制)

1192: [HNOI2006]鬼谷子的钱袋 Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 3223 Solved: 2333Descriptio 鬼谷子非常聪明&#xff0c;正因为这样&#xff0c;他非常繁忙&#xff0c;经常有各诸侯车的特派员前来向他咨询时政。有一天&#xff0c;他在咸阳游历的时候&…

聚类(Clustering): K-means算法

聚类(Clustering): K-means算法 1.归类: 聚类(clustering)属于非监督学习(unsupervised learning) 无类别标记( class label) 3. K-means 算法&#xff1a; 3.1 Clustering 中的经典算法&#xff0c;数据挖掘十大经典算法之一 3.2 算法接受参数 k &#xff1b;然后将事先输入…

智慧交通day02-车流量检测实现12:基于yoloV3的目标检测

在本章节代码编写中&#xff0c;发现之前的代码所处的环境是python3&#xff0c;因此导致了cv2.dnn.readNetFromDarknet()在代码运行中导致了i[0]的获值失败&#xff0c;故总结如下&#xff1a; cv2.dnn.readNetFromDarknet()在python3上遇到的问题_李大狗的读研日记-CSDN博客…