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试图锁住互斥量,而该互斥量,又被其他线程锁住(占用),则该线程的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,源代码如下:

、#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");
  //把互斥量mutex加锁,以确保同一时间只有该线程可以访问msg中的数据
  pthread_mutex_lock(&mutex);
  while(strcmp("end\n", msg) != 0)
  {
    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秒再继续循环,让其他线程有执行的机会
    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);
}

运行结果如下:

程序分析:

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

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

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

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

感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!

(0)

相关推荐

  • linux多线程编程详解教程(线程通过信号量实现通信代码)

    线程分类 线程按照其调度者可以分为用户级线程和核心级线程两种. (1)用户级线程 用户级线程主要解决的是上下文切换的问题,它的调度算法和调度过程全部由用户自行选择决定,在运行时不需要特定的内核支持.在这里,操作系统往往会提供一个用户空间的线程库,该线程库提供了线程的创建.调度.撤销等功能,而内核仍然仅对进程进行管理.如果一个进程中的某一个线程调用了一个阻塞的系统调用,那么该进程包括该进程中的其他所有线程也同时被阻塞.这种用户级线程的主要缺点是在一个进程中的多个线程的调度中无法发挥多处理器的优势.

  • Linux下的多线程编程(三)

    下面先来一个实例.我们通过创建两个线程来实现对一个数的递加. 或许这个实例没有实际运用的价值,但是稍微改动一下,我们就可以用到其他地方去拉. 下面是我们的代码: /*thread_example.c : c multiple thread programming in linux *author : falcon *E-mail : tunzhj03@st.lzu.edu.cn */ #include <pthread.h> #include <stdio.h> #include

  • linux多线程编程(五)

    线程 线程是计算机中独立运行的最小单位,运行时占用很少的系统资源.可以把线程看成是操作系统分配CPU时间的基本单元.一个进程可以拥有一个至多个线程.它线程在进程内部共享地址空间.打开的文件描述符等资源.同时线程也有其私有的数据信息,包括:线程号.寄存器(程序计数器和堆栈指针).堆栈.信号掩码.优先级.线程私有存储空间. 为什么有了进程的概念后,还要再引入线程呢?使用多线程到底有哪些好处?什么的系统应该选用多线程? 使用多线程的理由之一是和进程相比,它是一种非常"节俭"的多任务操作方式.

  • Linux多线程编程(一)

    一.什么是线程? 线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源. 二.什么时候使用多线程?     当多个任务可以并行执行时,可以为每个任务启动一个线程. 三.线程的创建     使用pthread_create函数. #include<pthread.h> int pthread_create

  • Linux多线程锁属性设置方法

    互斥锁是Linux下多线程资源保护的常用手段,但是在时序复杂的情况下,很容易会出现死锁的情况. 可以通过设置锁的属性,避免同一条线程重复上锁导致死锁的问题. 通过int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int type)接口设置 一般是以下四种属性: PTHREAD_MUTEX_NORMAL This type of mutex does not detect deadlock. A thread attempting t

  • Linux C中多线程与volatile变量

    Linux C中多线程与volatile变量 volatile 修饰的变量表示改变量的值是易变的,编译器不对其进行优化,访问该变量的时候不会从寄存器读取, 而是直接从内存读取变量. 在多线程环境下,每个线程都有一个独立的寄存器,用于保存当前执行的指令.假设我们定义了一个全局变量,每个线程都会访问这个全局变量,这时候线程的寄存器可能会存储全量变量的当前值用于后续的访问.当某个线程修改了全局变量的值时,系统会立即更新该线程寄存器中对应的值,其他线程并不知道这个全局变量已经修改,可能还是从寄存器中获取

  • 浅析Linux下一个简单的多线程互斥锁的例子

    复制代码 代码如下: #include <stdio.h>#include <pthread.h>pthread_mutex_t Device_mutex ;int count=0;void thread_func1(){   while(1)   {       pthread_mutex_lock(&Device_mutex);       printf("thread1: %d\n",count);       pthread_mutex_unlo

  • 详解Linux多线程使用信号量同步

    信号量.同步这些名词在进程间通信时就已经说过,在这里它们的意思是相同的,只不过是同步的对象不同而已.但是下面介绍的信号量的接口是用于线程的信号量,注意不要跟用于进程间通信的信号量混淆. 一.什么是信号量 线程的信号量与进程间通信中使用的信号量的概念是一样,它是一种特殊的变量,它可以被增加或减少,但对其的关键访问被保证是原子操作.如果一个程序中有多个线程试图改变一个信号量的值,系统将保证所有的操作都将依次进行. 而只有0和1两种取值的信号量叫做二进制信号量,在这里将重点介绍.而信号量一般常用于保护

  • Linux多线程环境下 关于进程线程终止函数总结

    pthread_kill: pthread_kill与kill有区别,是向线程发送signal.,大部分signal的默认动作是终止进程的运行,所以,我们才要用signal()去抓信号并加上处理函数. int pthread_kill(pthread_t thread, int sig); 向指定ID的线程发送sig信号,如果线程代码内不做处理,则按照信号默认的行为影响整个进程,也就是说,如果你给一个线程发送了SIGQUIT,但线程却没有实现signal处理函数,则整个进程退出. pthread

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

    本文将会给出互斥量的详细解说,并用一个互斥量解决上一篇文章中,要使用两个信号量才能解决的只有子线程结束了对输入的处理和统计后,主线程才能继续执行的问题. 一.什么是互斥量 互斥量是另一种用于多线程中的同步访问方法,它允许程序锁住某个对象,使得每次只能有一个线程访问它.为了控制对关键代码的访问,必须在进入这段代码之前锁住一个互斥量,然后在完成操作之后解锁. 二.互斥量的函数的使用 它们的定义与使用信号量的函数非常相似,它们的定义如下: #include <pthread.h> int pthre

  • 总结java多线程之互斥与同步解决方案

    一.线程互斥与同步 互斥:指的是多个线程不能同时访问共享变量 同步:指的是多个线程按指定的顺序执行操作 在同时有多个线程运行过程中,如何达到互斥和同步呢? 加锁即可 在此使用黑马笔记中room例子来说明锁.(ps: 以前就了解锁,但总会记乱,发现使用形象化记忆后就很清楚) 解决互斥 锁就相当于上图的房子,里面放着会被并发访问的共享变量 此时绿色区域(owner)无线程,此时多个线程想并发访问房子里的共享变量,那么只允许其中一个线程进入房子访问,并把房门锁上. 剩下的没有拿到锁的线程只能在entr

  • C++ 多线程之互斥量(mutex)详解

    目录 std::mutex std::recursive_mutex std::time_mutex std::recursive_timed_mutex std::shared_mutex std::shared_timed_mutex 总结 C++ 11中的互斥量,声明在 <mutex> 头文件中,互斥量的使用可以在各种方面,比较常用在对共享数据的读写上,如果有多个线程同时读写一个数据,那么想要保证多线程安全,就必须对共享变量的读写进行保护(上锁),从而保证线程安全. 互斥量主要有四中类型

  • C++详解多线程中的线程同步与互斥量

    目录 线程同步 互斥量 线程同步 /* 使用多线程实现买票的案例. 有3个窗口,一共是100张票. */ #include <stdio.h> #include <pthread.h> #include <unistd.h> // 全局变量,所有的线程都共享这一份资源. int tickets = 100; void * sellticket(void * arg) { // 卖票 while(tickets > 0) { usleep(6000); //微秒 p

  • Linux线程管理必备:解析互斥量与条件变量的详解

    做过稍微大一点项目的人都知道,力求程序的稳定性和调度的方便,使用了大量的线程,几乎每个模块都有一个专门的线程处理函数.而互斥量与条件变量在线程管理中必不可少,任务间的调度几乎都是由互斥量与条件变量控制.互斥量的实现与进程中的信号量(无名信号量)是类似的,当然,信号量也可以用于线程,区别在于初始化的时候,其本质都是P/V操作.编译时,记得加上-lpthread或-lrt哦. 有关进程间通信(消息队列)见:进程间通信之深入消息队列的详解 一.互斥量 1. 初始化与销毁: 对于静态分配的互斥量, 可以

  • 对python多线程中互斥锁Threading.Lock的简单应用详解

    一.线程共享进程资源 每个线程互相独立,相互之间没有任何关系,但是在同一个进程中的资源,线程是共享的,如果不进行资源的合理分配,对数据造成破坏,使得线程运行的结果不可预期.这种现象称为"线程不安全". 实例如下: #-*- coding: utf-8 -*- import threading import time def test_xc(): f = open("test.txt","a") f.write("test_dxc&quo

  • Linux多线程中fork与互斥锁过程示例

    目录 问题提出: (一)初次尝试 (二)理性分析 (三)解决问题 (1)使用pthread_join() (2)使用phread_atfork()注册一个fork之前的判断 问题提出: 我们有这样一个问题:在一个多线程程序中创建子进程并且让子线程和子进程去获取一把全局变量的锁,输出子线程得到锁,然后解锁,子进程拿到锁,然后解锁: (一)初次尝试 代码: #include <stdio.h> #include <unistd.h> #include <pthread.h>

随机推荐