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

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

二、互斥量的函数的使用

它们的定义与使用信号量的函数非常相似,它们的定义如下:
 
#include <pthread.h>
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr); int pthread_mutex_lock(pthread_mutex_t *mutex); int pthread_mutex_unlock(pthread_mutex_t *mutex); int pthread_mutex_destroy(pthread_mutex_t *mutex);
它们的意义就如它们的名字所示的那样,成功时返回0,失败时返回错误代码,它们并不设置errno。
pthread_mutex_init函数中的参数mutexattr指定互斥量的属性,在这里我们并不关心互斥量的属性,所以把它设置为NULL,使用默认属性即可。同样的,pthread_mutex_lock和pthread_mutex_unlock都是原子操作。

注意,使用互斥量的默认属性,如果程序试图对一个已经加锁的互斥量调用pthread_mutex_lock,程序就会阻塞,而又因为拥有互斥量的这个线程正是现在被阻塞的线程,所以这个互斥量就永远不会被解锁,也就是说,程序就会进入死锁的状态。在使用时要多加注意,确保在同一个线程中,对加锁的互斥再次进行加锁前要对其进行解锁。

三、使用互斥量进行线程同步

下面以一个简单的多线程程序来演示如何使用互斥量来进行线程同步。在主线程中,我们创建子线程,并把数组msg作为参数传递给子线程,然后主线程调用函数pthread_mutex_lock对互斥量加锁,等待输入,输入完成后,调用函数pthread_mutex_unlock对互斥量解锁,从而使线程函数中的对互斥量加锁的pthread_mutex_lock函数返回并执行子线程中的代码。线程函数在把字符串的小写字母变成大写并统计输入的字符数量之后,它调用pthread_mutex_unlock对互斥量解锁,使主线程能够继续获得互斥量(即对其加锁函数返回),再次执行输入功能直到主线程再次调用pthread_mutex_unlock对其解锁,一直如此重复,直到输入end。

源文件为lockthread.c,源代码如下:

 
#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 512 int 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_func
res = 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");
while(strcmp("end\n", msg) != 0)
{
//把互斥量mutex加锁,以确保同一时间只有该线程可以访问msg中的数据
pthread_mutex_lock(&mutex);
if(strncmp("TEST", msg, 4) == 0)
{
strcpy(msg, "copy_data\n");
}
else
{
fgets(msg, MSG_SIZE, stdin);
}
//把互斥量mutex解锁,让其他的线程可以访问msg中的数据
pthread_mutex_unlock(&mutex);
sleep(1);//休眠1秒再继续循环,让其他线程有执行的机会
}
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);
}
运行结果如下:



程序分析:

这个程序的工作流程已经说得非常清楚了,这里先来说说在main函数和线程函数thread_func中while循环中的sleep(1)语句的作用。可能很多人会认为这个sleep(1)是为了让子线程完成其处理和统计功能,所以要让主线程休眠1秒钟来等待子线程的处理统计工作的完成。的确在这里子线程进行的工作十分简单,1秒钟内的确可以处理统计完毕。但是这里的sleep(1)并不是为了实现这个功能,这两个循环中的sleep(1)是为了让其他的线程有机会被执行到,如果在一次的加锁和解锁之间没有这条语句的话,则当前的线程将会一直在循环中获得互斥量,因为其他的线程没有执行它的代码的时间,所以就要用这样的一条语句来给其他的线程一个运行的机会。如果子线程的执行时间超过1秒,这个程序还是会正常运行。

以这个例子来说,在主线程中,当输入数据完毕并对互斥量解锁之后,并不马上循环对其加锁,此时子线程就有了执行的机会,它会对互斥量进行加锁,同样地,当它处理统计完输入的数据后,它在进入下一次循环前,也休眠1秒,让主线程有机会再次运行。而主线程什么时候能够执行,取决于子线程何时对互斥量进行解锁。因为如果子线程拥有(锁住)互斥量,则主线程中函数pthread_mutex_lock就不会返回,使主线程处于阻塞状态。

换句话来说,就是
只有子线程结束了对输入的处理和统计后,主线程才能继续执行,向msg中写入数据。看到这里,你应该知道之前在使用信号量时,我们多用一个信号量也是为了达到这个目的。所以当我们输入TEST时,程序有两个输入,但还是能正常运行,同样解决了之前使用一个信号量时所带来的问题。

信号量和互斥量的作用都是保护代码段的互斥设备,它们也非常相似。但在本例中,与使用信号量相比,实现同样的功能,如果使用信号量的话,则需要两个信号量,而使用互斥量的话,只需要一个。可以说在本例中,使用互斥量更简单。但是我觉得使用互斥量更容易犯错,我们可以看到在这个例子中,我们需要使用sleep语句来让其他线程获得执行的机会,但是在使用信号量的程序,它并不需要使用sleep,相对来说比较直观。我知道可能是我的实现方法不好,但是对于使用互斥量来说,我想了很久也想不到不使用sleep的方法。

Linux多线程——使用互斥量同步线程的更多相关文章

  1. Linux多线程--使用互斥量同步线程【转】

    本文转载自:http://blog.csdn.net/ljianhui/article/details/10875883 前文再续,书接上一回,在上一篇文章:Linux多线程——使用信号量同步线程中, ...

  2. Linux:使用互斥量进行线程同步

    基础知识 同步概念 所谓同步,即同时起步,协调一致.不同的对象,对"同步"的理解方式略有不同.如,设备同步,是指在两个设备之间规定一个共同的时间参考:数据库同步,是指让两个或多个数 ...

  3. 【Linux】Mutex互斥量线程同步的例子

    0.互斥量  Windows下的互斥量 是个内核对象,每次WaitForSingleObject和ReleaseMutex时都会检查当前线程ID和占有互斥量的线程ID是否一致. 当多次Wait**时就 ...

  4. 多线程相关------互斥量Mutex

    互斥量(Mutex) 互斥量是一个可以处于两态之一的变量:解锁和加锁.只有拥有互斥对象的线程才具有访问资源的权限.并且互斥量可以用于不同进程中的线程的互斥访问. 相关函数: CreateMutex用于 ...

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

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

  6. C++多线程,互斥,同步

    同步和互斥 当有多个线程的时候,经常需要去同步这些线程以访问同一个数据或资源.例如,假设有一个程序,其中一个线程用于把文件读到内存,而另一个线程用于统计文件中的字符数.当然,在把整个文件调入内存之前, ...

  7. Linux系统编程 —互斥量mutex

    互斥量mutex 前文提到,系统中如果存在资源共享,线程间存在竞争,并且没有合理的同步机制的话,会出现数据混乱的现象.为了实现同步机制,Linux中提供了多种方式,其中一种方式为互斥锁mutex(也称 ...

  8. Linux多线程(二)(线程等待,退出)

    1. 线程的等待退出 1.1. 等待线程退出 线程从入口点函数自然返回,或者主动调用pthread_exit()函数,都可以让线程正常终止 线程从入口点函数自然返回时,函数返回值可以被其它线程用pth ...

  9. Linux多线程实践(三)线程的基本属性设置API

    POSIX 线程库定义了线程属性对象 pthread_attr_t ,它封装了线程的创建者能够訪问和改动的线程属性.主要包含例如以下属性: 1. 作用域(scope) 2. 栈尺寸(stack siz ...

随机推荐

  1. Ext.Date 方法

    1.Ext.Date.add(date,interval,value); 提供执行基本日期运算的简便方法; date 日期对象, interval 一个有效的日期间隔枚举值, value 向当前日期上 ...

  2. 不用找了,比较全的signalR例子已经为你准备好了.

    这几天想着将一个winform的工具上线到web上,因为对时时性的要求比较高,找朋友咨询了一下推荐了SignlarR 框架,比较强大.昨天才看到,今天研究了一下将里面的例子都拿出来共享. 最全的参考: ...

  3. Samara SAU ACM ICPC 2013-2014 Quarterfinal Qualification Contest

    A: 简单题,因为题目中说了不会有数据相同: #include<cstdio> #include<algorithm> #define maxn 200005 using na ...

  4. Cxf soap协议改成1.2

    在和.net做联调的时候,报错: A SOAP 1.2 message is not valid when sent to a SOAP 1.1 only endpoint. 看来是soap协议不匹配 ...

  5. 在线CSS圆角生成器

    http://www.paibaidu.com/demo/CSSBorder/CSSBorder.html

  6. linux下USB串口,minicom

    [一].驱动相关说明: 如果直接使用串口线,而没有用到USB转串口设备,就不需要安装驱动. 如果使用了USB转串口,一般情况下也不需要安装驱动了,目前linux系统已经包含了该驱动,可以自动识别,亦可 ...

  7. TCP/IP 目录导航

    用了近二十天的时间,把一本800页的书看完,感觉收获还是很大的.对网络,对这些协议有了深刻的认识! 知道了路由器与交换机的区别. 知道了IP地址的特点. 知道了网络的分层,物理层,数据链路层,网络层, ...

  8. bzoj3143

    之前我们曾经用dp解决过数学期望问题,这次我们用的是解方程的方法首先在编号之前,肯定要求出每条边的期望经过次数然后可以转化为求边端点的期望次数这种做法我一开始接触是noip2013的初赛问题求解,是类 ...

  9. BZOJ3036: 绿豆蛙的归宿&Wikioi2488:绿豆蛙的归宿

    3036: 绿豆蛙的归宿 Time Limit: 2 Sec  Memory Limit: 128 MBSubmit: 108  Solved: 73[Submit][Status] Descript ...

  10. 【转】微信Android SDK示例代码及运行方法

    原文网址:http://blog.csdn.net/icyfox_bupt/article/details/23742217 最近在研究微信SDK,无奈网上好使的教程太少,对于程序员来说最好的东西,一 ...