Linux C 线程同步实例分析


先举出一个同步的经典例子,生产者消费者. 让我感到欣慰的是在这个代码中,我看到了《OS原理》中PV原语的实现,挺激动的。

简单的生产者 消费者模型 

  1. */  
  2. #include <stdio.h>  
  3. #include <string.h>  
  4. #include <semaphore.h>  
  5. #include <pthread.h>  
  6. #include <sys/types.h>  
  7. #include <unistd.h>  
  8.   
  9. /*封装 P/V 操作*/  
  10. void P(sem_t* sem)  
  11. {  
  12.     if(sem_wait(sem))  
  13.         perror("P operating error");  
  14. }  
  15. void V(sem_t* sem)  
  16. {  
  17.     if(sem_post(sem))  
  18.         perror("V operating error");  
  19. }  
  20.   
  21. /*定义共享缓冲区*/  
  22. static char share_buf[50];  
  23. /*定义两个信号量以及其初始化函数*/  
  24. sem_t empty_sem;  
  25. sem_t full_sem;  
  26. void init_sem()  
  27. {  
  28.     sem_init(&empty_sem,0,1);  
  29.     sem_init(&full_sem,0,0);  
  30. }  
  31.   
  32. /*生产者*/  
  33. void* produce(void* arg)  
  34. {  
  35.     char buf[50]={0};  
  36.     while(1){  
  37.         printf("Input message>>\n");  
  38.         fgets(buf,sizeof(buf),stdin);  
  39.         printf("Produce item is>>%s",buf);  
  40.   
  41.         /*将消息放入缓冲区*/  
  42.         P(&empty_sem);  
  43.         memcpy(share_buf,buf,sizeof(buf));  
  44.         V(&full_sem);  
  45.     }  
  46.     return NULL;     
  47. }  
  48.   
  49. /*消费者*/  
  50. void* consumer(void* arg)  
  51. {  
  52.     char buf[50]={0};  
  53.     while(1){  
  54.         P(&full_sem);  
  55.         memcpy(buf,share_buf,sizeof(share_buf));  
  56.         V(&empty_sem);  
  57.   
  58.                 /*显示获得信息*/  
  59.         printf("Consume item is<<%s",buf);  
  60.     }  
  61.     return NULL;  
  62. }  
  63.   
  64. int main()  
  65. {     
  66.     pthread_t produce_tid;  
  67.     pthread_t consumer_tid;  
  68.   
  69.     init_sem();  
  70.   
  71.     pthread_create(&produce_tid,NULL,produce,NULL);  
  72.     pthread_create(&consumer_tid,NULL,consumer,NULL);  
  73.      
  74.     pthread_join(produce_tid,NULL);  
  75.     pthread_join(consumer_tid,NULL);  
  76.      
  77.     return 0;  
  78. }  

上面的是一个很正规的实现,体现不出多个线程同时违规访问临界资源的危险性,为了体现出不使用或者错误使用信号量,而导致多个线程同时访问临界资源所产生的后果,我修改了一下程序:

  1. /* 
  2. *简单的生产者 消费者模型 
  3. */  
  4. #include <stdio.h>   
  5. #include <string.h>   
  6. #include <semaphore.h>   
  7. #include <pthread.h>   
  8. #include <sys/types.h>   
  9. #include <unistd.h>   
  10.   
  11. /*封装 P/V 操作*/  
  12. void P(sem_t* sem)  
  13. {  
  14.     if(sem_wait(sem))  
  15.         perror("P operating error");  
  16. }  
  17. void V(sem_t* sem)  
  18. {  
  19.     if(sem_post(sem))  
  20.         perror("V operating error");  
  21. }  
  22.   
  23. /*定义共享缓冲区*/  
  24. static char share_buf[50];  
  25. /*定义两个信号量以及其初始化函数*/  
  26. sem_t empty_sem;  
  27. sem_t full_sem;  
  28. void init_sem()  
  29. {  
  30.     sem_init(&empty_sem,0,1);  
  31.     sem_init(&full_sem,0,0);  
  32. }  
  33.   
  34. /*生产者*/  
  35. void* produce(void* arg)  
  36. {  
  37.     int i=*((int*)arg);  
  38.     char buf[50]={0};  
  39.     while(1){  
  40.         printf("Thread %d input message>>",i);  
  41.         fflush(stdout);  
  42.         fgets(buf,sizeof(buf),stdin);  
  43.         printf("Thread %d Produce item is>>%s",i,buf);  
  44.         fflush(stdout);  
  45.   
  46.         /*将消息放入缓冲区*/  
  47.         P(&empty_sem);  
  48.         memcpy(share_buf,buf,sizeof(buf));  
  49.         V(&full_sem);  
  50.         sleep(1);    /*睡眠一会,让另一个线程执行*/  
  51.     }  
  52.     return NULL;     
  53. }  
  54.   
  55. /*消费者*/  
  56. void* consumer(void* arg)  
  57. {  
  58.     char buf[50]={0};  
  59.     while(1){  
  60.         P(&full_sem);  
  61.         memcpy(buf,share_buf,sizeof(share_buf));  
  62.         V(&empty_sem);  
  63.         V(&empty_sem);      /*注意这里,我多增加信号量,使两个线程都可以访问临界资源*/  
  64.   
  65.                 /*显示获得信息*/  
  66.         printf("Consume item is<<%s",buf);  
  67.     }  
  68.     return NULL;  
  69. }  
  70.   
  71. int main()  
  72. {     
  73.     pthread_t produce_tid;  
  74.     pthread_t produce1_tid;  
  75.     pthread_t consumer_tid;  
  76.      
  77.     init_sem();  
  78.     int i=1,j=2;  
  79.     pthread_create(&produce_tid,NULL,produce,(void*)&i);  
  80.     pthread_create(&produce1_tid,NULL,produce,(void*)&j);  
  81.     pthread_create(&consumer_tid,NULL,consumer,NULL);  
  82.      
  83.   
  84.     pthread_join(produce_tid,NULL);  
  85.     pthread_join(consumer_tid,NULL);  
  86.      
  87.     return 0;  
  88. }  

把该代码运行一下,你就会看到,除第一次外,都是先输出“Thread 1 input message>>”,然后输出的是另一个进程的"Pthread 2 produce item is >>.."

可见,由于两个线程可以同时访问临界资源,从而导致程序执行顺序的混乱。还要考虑到另外一点,这两个线程都是等待
我们从键盘输入,所以一次只有一个线程获得数据然后写入,但是如果这两个线程是从文件中读取数据的话,临界资源的
内容就会混乱,从而导致程序运行错误,出现的结果会令人意想不到。

  • 1
  • 2
  • 3
  • 下一页

相关内容