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

2019-10-13 19:21:17王冬梅

本文将会给出互斥量的详细解说,并用一个互斥量解决上一篇文章中,要使用两个信号量才能解决的只有子线程结束了对输入的处理和统计后,主线程才能继续执行的问题。

一、什么是互斥量

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

二、互斥量的函数的使用

它们的定义与使用信号量的函数非常相似,它们的定义如下:

#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] = {''}; 
  //初始化互斥量,使用默认的互斥量属性 
  res = pthread_mutex_init(&mutex, NULL); 
  if(res != 0) 
  { 
    perror("pthread_mutex_init failedn"); 
    exit(EXIT_FAILURE); 
  } 
  //创建子线程,并把msg作为线程函数的参数传递给thread_func 
  res = pthread_create(&thread, NULL, thread_func, msg); 
  if(res != 0) 
  { 
    perror("pthread_create failedn"); 
    exit(EXIT_FAILURE); 
  } 
  //输入字符串,以串‘end'结束 
  printf("Input some test. Enter 'end' to finishn"); 
  //把互斥量mutex加锁,以确保同一时间只有该线程可以访问msg中的数据 
  pthread_mutex_lock(&mutex); 
  while(strcmp("endn", msg) != 0) 
  { 
    if(strncmp("TEST", msg, 4) == 0) 
    { 
      strcpy(msg, "copy_datan"); 
    } 
    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 failedn"); 
    exit(EXIT_FAILURE); 
  } 
  printf("Thread joinedn"); 
  //清理互斥量 
  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("endn", msg) != 0) 
  { 
    //把小写字母变成大写 
    for(i = 0; ptr[i] != ''; ++i) 
    { 
      if(ptr[i] >= 'a' && ptr[i] <='z') 
      { 
        ptr[i] -= 'a' - 'A'; 
      } 
    } 
    printf("You input %d charactersn", i-1); 
    printf("To uppercase: %sn", ptr); 
    //把互斥量mutex解锁,让其他的线程可以访问msg中的数据 
    pthread_mutex_unlock(&mutex); 
    sleep(1);//休眠1秒再继续循环,让其他线程有执行的机会 
    pthread_mutex_lock(&mutex); 
  } 
  pthread_mutex_unlock(&mutex); 
  //退出线程 
  pthread_exit(NULL); 
}