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

2019-10-13 19:13:32王振洲

#include <unistd.h> 
#include <pthread.h> 
#include <semaphore.h> 
#include <stdlib.h> 
#include <stdio.h> 
#include <string.h> 
 
//线程函数 
void *thread_func(void *msg); 
sem_t sem;//信号量 
 
#define MSG_SIZE 512 
 
int main() 
{ 
  int res = -1; 
  pthread_t thread; 
  void *thread_result = NULL; 
  char msg[MSG_SIZE]; 
  //初始化信号量,其初值为0 
  res = sem_init(&sem, 0, 0); 
  if(res == -1) 
  { 
    perror("semaphore intitialization failedn"); 
    exit(EXIT_FAILURE); 
  } 
  //创建线程,并把msg作为线程函数的参数 
  res = pthread_create(&thread, NULL, thread_func, msg); 
  if(res != 0) 
  { 
    perror("pthread_create failedn"); 
    exit(EXIT_FAILURE); 
  } 
  //输入信息,以输入end结束,由于fgets会把回车(n)也读入,所以判断时就变成了“endn” 
  printf("Input some text. Enter 'end'to finish...n"); 
  while(strcmp("endn", msg) != 0) 
  { 
    fgets(msg, MSG_SIZE, stdin); 
    //把信号量加1 
    sem_post(&sem); 
  } 
 
  printf("Waiting for thread to finish...n"); 
  //等待子线程结束 
  res = pthread_join(thread, &thread_result); 
  if(res != 0) 
  { 
    perror("pthread_join failedn"); 
    exit(EXIT_FAILURE); 
  } 
  printf("Thread joinedn"); 
  //清理信号量 
  sem_destroy(&sem); 
  exit(EXIT_SUCCESS); 
} 
 
void* thread_func(void *msg) 
{ 
  //把信号量减1 
  sem_wait(&sem); 
  char *ptr = msg; 
  while(strcmp("endn", msg) != 0) 
  { 
    int i = 0; 
    //把小写字母变成大写 
    for(; 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); 
    //把信号量减1 
    sem_wait(&sem); 
  } 
  //退出线程 
  pthread_exit(NULL); 
} 

运行结果如下:

从运行的结果来看,这个程序的确是同时在运行两个线程,一个控制输入,另一个控制处理统计和输出。

四、分析此信号量同步程序的缺陷

但是这个程序有一点点的小问题,就是这个程序依赖接收文本输入的时间足够长,这样子线程才有足够的时间在主线程还未准备好给它更多的单词去处理和统计之前处理和统计出工作区中字符的个数。所以当我们连续快速地给它两组不同的单词去统计时,子线程就没有足够的时间支执行,但是信号量已被增加不止一次,所以字符统计线程(子线程)就会反复处理和统计字符数目,并减少信号量的值,直到它再次变成0为止。

为了更加清楚地说明上面所说的情况,修改主线程的while循环中的代码,如下:

printf("Input some text. Enter 'end'to finish...n"); 
while(strcmp("endn", msg) != 0) 
{ 
  if(strncmp("TEST", msg, 4) == 0) 
  { 
    strcpy(msg, "copy_datan"); 
    sem_post(&sem); 
  } 
  fgets(msg, MSG_SIZE, stdin); 
  //把信号量加1 
  sem_post(&sem); 
}