Linux下多线程编程中信号量介绍及简单使用

线程的信号量是一种特殊的变量,它可以被增加或减少,但对其的关键访问被保证是原子操作。如果一个程序中有多个线程试图改变一个信号量的值,系统将保证所有的操作都将依次进行。信号量一般常用于保护一段代码,使其每次只被一个执行线程运行。信号量是用来调协线程对共享资源的访问的。

通过使用信号量可以很好的完成线程同步。两个线程同时监视同一个信号量。A线程增加信号量的值,B线程减少信号量的值。当A线程增加信号量大于0时,B线程的等待信号量就会触发,每触发一次将信号量减1,直到将信号量减为0,B线程继续等待A线程增加信号量。

信号量和互斥锁(mutex)的区别:互斥锁只允许一个线程进入临界区,而信号量允许多个线程同时进入临界区。

信号量用在多线程多任务同步的,一个线程完成了某一个动作就通过信号量告诉别的线程,别的线程再进行某些动作。而互斥锁是用在多线程多任务互斥的,一个线程占用了某一个资源,那么别的线程就无法访问,直到这个线程unlock,其他的线程才开始可以利用这个资源。比如对全局变量的访问,有时要加锁,操作完了,在解锁。有的时候锁和信号量会同时使用的。

信号量:只要信号量的value大于0,其他线程就可以sem_wait成功,成功后信号量的value减1。若value值不大于0,则sem_wait使得线程阻塞,直到sem_post释放后value值加1,但是sem_wait返回之前还是会将此value值减1.

如果信号量的值大于0表示可用的资源数,小于0表示阻塞的线程数。

互斥锁: 只要被锁住,其他任何线程都不可以访问被保护的资源,也就是说,信号量不一定是锁定某一个资源,而是流程上的概念,比如:有A,B两个线程,B线程要等A线程完成某一任务以后再进行自己下面的步骤,这个任务并不一定是锁定某一资源,还可以是进行一些计算或者数据处理之类。而线程互斥量则是“锁住某一资源”的概念,在锁定期间内,其他线程无法对被保护的数据进行操作。在有些情况下两者可以互换。

信号量是一个特殊类型的变量,它可以被增加或减少,但对它的访问都会被保证是原子操作,即使在一个多线程程序中也是如此。也就是说,如果一个程序中有两个或多个线程试图改变一个信号量的值,系统将保证所有的操作都将依次进行。如果换成普通的变量,来自同一个程序中的不同线程的冲突操作将会导致不确定的操作。

两种信号量:二进制信号量和计数信号量。二进制信号量只有0和1两种取值,而计数信号量则有更大的取值范围。如果某个共享资源只能被一个线程访问,那么二进制信号量则是最好的打算;如果有多个线程需要访问共享资源呢,使用计数信号量则是个好的主意。

互斥锁只有0,1两中状态,适合于线程对共享资源的独占访问,很多时候每个资源可以同时被有限的线程访问,此时互斥锁将无法满足;条件变量同步也同样存在这种问题。信号量实际是一种非负整型计数器,可以很好的控制线程之间资源访问,互斥锁能实现的功能,信号量同样可以。

信号量控制资源共享主要是PV原语操作, PV原语是对整数计数器信号量sem的操作。一次P操作使sem减一,而一次V操作使sem加一。进程(或线程)根据信号量的值来判断是否对公共资源具有访问权限。当信号量sem的值大于等于零时,该进程(或线程)具有公共资源的访问权限;相反,当信号量sem的值小于零时,该进程(或线程)就将阻塞直到信号量sem的值大于等于0为止。

信号量的函数都以sem_开头,线程中使用的基本信号量函数有4个,它们都声明在头文件semaphore.h中。

sem_init(sem_t*sem, int pshared, unsigned int value):该函数用于创建信号量。初始化一个定位在sem的匿名信号量。value参数指定信号量的初始值。pshared参数指明信号量是由进程内线程共享,还是由进程之间共享。如果pshared的值为0,那么信号量将被进程内的线程共享,并且应该放置在所有线程都可见的地址上(如全局变量,或者堆上动态分配的变量)。

sem_wait(sem_t*sem):该函数用于以原子操作的方式将信号量的值减1。(原子操作就是,如果两个线程企图同时给一个信号量加1或减1,它们之间不会互相干扰。)但它永远会先等待该信号量为一个非零值才开始做减法。也就是说,如果你对一个值为2的信号量调用sem_wait(),线程将会继续执行,这信号量的值将减到1。如果对一个值为0的信号量调用sem_wait(),这个函数就会等待直到有其它线程增加了这个值使它不再是0为止。如果有两个线程都在sem_wait()中等待同一个信号量变成非零值,那么当它被第三个线程增加一个“1”时,等待线程中只有一个能够对信号量做减法并继续执行,另一个还将处于等待状态。被用来阻塞当前线程直到信号量sem的值大于0,解除阻塞后将sem的值减一,表明公共资源经使用后减少。

sem_post(sem_t*sem):该函数用于以原子操作的方式将信号量的值加1。用来增加信号量的值当有线程阻塞在这个信号量上时,调用这个函数会使其中的一个线程不再阻塞,选择机制同样是由线程的调度策略决定的。它信号量的值加1同时发出信号来唤醒等待的线程。

sem_destroy:该函数用于对用完的信号量的清理。用来释放信号量sem。

下面是从其他文章中copy的测试代码,详细内容介绍可以参考对应的reference:

test_thread_sem.cpp:

[cpp] view plaincopyprint?

  1. // reference: https://software.intel.com/zh-cn/blogs/2011/12/02/linux-3  
  2. #include <iostream>  
  3. #include <pthread.h>  
  4. #include <semaphore.h>  
  5. #include <string.h>  
  6.   
  7. int g_Flag = 0;  
  8. sem_t sem_mutex; // 用于互斥  
  9. sem_t sem_syn; // 用于同步  
  10.   
  11. void *thread1(void *arg);  
  12. void *thread2(void *arg);  
  13. int main()  
  14. {  
  15.     pthread_t tid1, tid2;  
  16.     int rc1, rc2;  
  17.   
  18.     sem_init(&sem_mutex, 0, 1);  
  19.     sem_init(&sem_syn, 0, 0);  
  20.     printf(" Inter main !\n");  
  21.   
  22.      rc2 = pthread_create(&tid2, NULL, thread2, NULL);  
  23.     if(rc2 != 0)  
  24.         printf(" %s, %d \n", __func__, strerror(rc2));  
  25.   
  26.     rc1 = pthread_create(&tid1, NULL, thread1, &tid2);  
  27.     if(rc1 != 0)  
  28.         printf(" %s, %d \n", __func__, strerror(rc1));  
  29.       
  30.     printf(" Leave main!\n\n");  
  31.   
  32.     sem_wait(&sem_syn); // 同步等待,阻塞  
  33.     exit(0);  
  34. }  
  35.   
  36. void *thread1(void *arg)  
  37. {  
  38.     pthread_t *ptid = NULL;  
  39.     printf(" Enter thread1\n");  
  40.     printf(" thread1 id: %u, g_Flag: %d \n", ( unsigned int )pthread_self(), g_Flag);  
  41.   
  42.     if(sem_wait( &sem_mutex ) != 0)  
  43.      {  
  44.         perror(" pthread1 sem_mutex\n");  
  45.     }  
  46.   
  47.     if(g_Flag == 2)  
  48.         sem_post(&sem_syn);  
  49.   
  50.      g_Flag = 1;  
  51.   
  52.     if(sem_post( &sem_mutex ) != 0)  
  53.     {  
  54.         perror("pthread1 sem_post\n");  
  55.     }  
  56.   
  57.     printf(" thread1 id: %u, g_Flag: %d \n",( unsigned int )pthread_self(), g_Flag);  
  58.     printf(" Leave thread1 \n\n");  
  59.   
  60.     ptid = (pthread_t*)arg;  
  61.     printf(" ptid = %u \n", *ptid);  
  62.     pthread_join(*ptid, NULL);  
  63.     pthread_exit(0 );  
  64. }  
  65.   
  66. void *thread2(void *arg)  
  67. {  
  68.     printf(" Enter thread2 !\n");  
  69.     printf(" thread2 id: %u , g_Flag: %d \n", ( unsigned int)pthread_self(), g_Flag);  
  70.   
  71.     if(sem_wait(&sem_mutex) != 0)  
  72.     {  
  73.         perror("thread2 sem_wait \n");  
  74.     }  
  75.   
  76.     if(g_Flag == 1)  
  77.         sem_post(&sem_syn);  
  78.   
  79.     g_Flag = 2;  
  80.   
  81.     if(sem_post( &sem_mutex ) != 0)  
  82.     {  
  83.         perror(" thread2 sem_post\n");  
  84.     }  
  85.   
  86.     printf(" thread2 id: %u , g_Flag: %d \n", (unsigned int)pthread_self(), g_Flag);  
  87.     printf("Leave thread2 \n\n");  
  88.   
  89.     pthread_exit(0);  
  90. }  

test_thread_sem1.cpp:

[cpp] view plaincopyprint?

  1. // reference: http://man7.org/linux/man-pages/man3/sem_wait.3.html  
  2.   
  3. #include <unistd.h>  
  4. #include <stdio.h>  
  5. #include <stdlib.h>  
  6. #include <semaphore.h>  
  7. #include <time.h>  
  8. #include <assert.h>  
  9. #include <errno.h>  
  10. #include <signal.h>  
  11.   
  12. sem_t sem;  
  13.   
  14. #define handle_error(msg) \  
  15.    do { perror(msg); exit(EXIT_FAILURE); } while (0)  
  16.   
  17. static void handler(int sig)  
  18. {  
  19.     write(STDOUT_FILENO, "sem_post() from handler\n", 24);  
  20.     if (sem_post(&sem) == -1) {  
  21.         write(STDERR_FILENO, "sem_post() failed\n", 18);  
  22.         _exit(EXIT_FAILURE);  
  23.     }  
  24. }  
  25.   
  26. int main(int argc, char *argv[])  
  27. {  
  28.     struct sigaction sa;  
  29.     struct timespec ts;  
  30.     int s;  
  31.   
  32.     if (argc != 3) {  
  33.         fprintf(stderr, "Usage: %s <alarm-secs> <wait-secs>\n", argv[0]);  
  34.         exit(EXIT_FAILURE);  
  35.     }  
  36.   
  37.     if (sem_init(&sem, 0, 0) == -1)  
  38.         handle_error("sem_init");  
  39.   
  40.     /* Establish SIGALRM handler; set alarm timer using argv[1] */  
  41.     sa.sa_handler = handler;  
  42.     sigemptyset(&sa.sa_mask);  
  43.     sa.sa_flags = 0;  
  44.     if (sigaction(SIGALRM, &sa, NULL) == -1)  
  45.         handle_error("sigaction");  
  46.   
  47.     alarm(atoi(argv[1]));  
  48.   
  49.     /* Calculate relative interval as current time plus 
  50.       number of seconds given argv[2] */  
  51.   
  52.     if (clock_gettime(CLOCK_REALTIME, &ts) == -1)  
  53.         handle_error("clock_gettime");  
  54.   
  55.     ts.tv_sec += atoi(argv[2]);  
  56.   
  57.     printf("main() about to call sem_timedwait()\n");  
  58.     while ((s = sem_timedwait(&sem, &ts)) == -1 && errno == EINTR)  
  59.         continue;       /* Restart if interrupted by handler */  
  60.   
  61.     /* Check what happened */  
  62.   
  63.     if (s == -1) {  
  64.         if (errno == ETIMEDOUT)  
  65.             printf("sem_timedwait() timed out\n");  
  66.         else  
  67.             perror("sem_timedwait");  
  68.     } else  
  69.         printf("sem_timedwait() succeeded\n");  
  70.   
  71.     exit((s == 0) ? EXIT_SUCCESS : EXIT_FAILURE);  
  72. }  
  73.   
  74. // ./test_thread_sem1 2 3  
  75. // ./test_thread_sem1 2 1  

test_thread_sem2.cpp:

[cpp] view plaincopyprint?

  1. // reference: https://mahaveerdarade.wordpress.com/2013/09/16/semaphores-in-linux-sem_wait-sem_post-code-examples-in-c/  
  2. #include <stdlib.h>  
  3. #include <pthread.h>   
  4. #include <stdio.h>  
  5. #include <semaphore.h>  
  6.    
  7. int cnt = 0;  
  8. int a[]={1,2,3,4,5,6,7,8,9};  
  9. char arr[]={'a','b','c','d','e','f','g','h','j'};  
  10. sem_t s1;  
  11.    
  12. void* pc(void* arg)  
  13. {  
  14.     int i=0;  
  15.     while(i<9) {  
  16.         sem_wait(&s1);  
  17.         while(cnt==0) {  
  18.             sem_post(&s1);  
  19.         }  
  20.         printf("%c", arr[i++]);  
  21.         cnt=0;  
  22.         sem_post(&s1);  
  23.     }  
  24. }  
  25.    
  26. void* pi(void* arg)  
  27. {  
  28.     int i=0;  
  29.     while(i<9) {  
  30.         sem_wait(&s1);  
  31.         while(cnt==1) {  
  32.             sem_post(&s1);  
  33.         }  
  34.         printf("%d",a[i++]);  
  35.         cnt=1;  
  36.         sem_post(&s1);  
  37.     }  
  38. }  
  39.    
  40. int main() {  
  41.     pthread_t t1,t2;  
  42.     sem_init(&s1, 0, 1);  
  43.     pthread_create(&t1, NULL, pc, NULL);  
  44.     pthread_create(&t2, NULL, pi, NULL);  
  45.     pthread_join(t1, NULL);  
  46.     pthread_join(t2, NULL);  
  47.     sem_destroy(&s1);  
  48.     return 0;  
  49. }  

test_thread_sem3.cpp:

[cpp] view plaincopyprint?

  1. // reference: http://www.amparo.net/ce155/sem-ex.html  
  2. /* Includes */  
  3. #include <unistd.h>     /* Symbolic Constants */  
  4. #include <sys/types.h>  /* Primitive System Data Types */   
  5. #include <errno.h>      /* Errors */  
  6. #include <stdio.h>      /* Input/Output */  
  7. #include <stdlib.h>     /* General Utilities */  
  8. #include <pthread.h>    /* POSIX Threads */  
  9. #include <string.h>     /* String handling */  
  10. #include <semaphore.h>  /* Semaphore */  
  11.   
  12. /* prototype for thread routine */  
  13. void* handler(void* ptr);  
  14.   
  15. /* global vars */  
  16. /* semaphores are declared global so they can be accessed  
  17.    in main() and in thread routine, 
  18.    here, the semaphore is used as a mutex */  
  19. sem_t mutex;  
  20. int counter; /* shared variable */  
  21.   
  22. int main()  
  23. {  
  24.     int i[2];  
  25.     pthread_t thread_a;  
  26.     pthread_t thread_b;  
  27.       
  28.     i[0] = 0; /* argument to threads */  
  29.     i[1] = 1;  
  30.       
  31.     sem_init(&mutex, 0, 1);      /* initialize mutex to 1 - binary semaphore */  
  32.                                  /* second param = 0 - semaphore is local */  
  33.                                    
  34.     /* Note: you can check if thread has been successfully created by checking return value of 
  35.        pthread_create */                                   
  36.     pthread_create(&thread_a, NULL, handler, (void*)&i[0]);  
  37.     pthread_create(&thread_b, NULL, handler, (void*)&i[1]);  
  38.       
  39.     pthread_join(thread_a, NULL);  
  40.     pthread_join(thread_b, NULL);  
  41.   
  42.     sem_destroy(&mutex); /* destroy semaphore */  
  43.                     
  44.     /* exit */    
  45.     exit(0);  
  46. } /* main() */  
  47.   
  48. void* handler(void* ptr)  
  49. {  
  50.     int x;   
  51.     x = *((int *)ptr);  
  52.     printf("Thread %d: Waiting to enter critical region...\n", x);  
  53.     sem_wait(&mutex);       /* down semaphore */  
  54.     /* START CRITICAL REGION */  
  55.     printf("Thread %d: Now in critical region...\n", x);  
  56.     printf("Thread %d: Counter Value: %d\n", x, counter);  
  57.     printf("Thread %d: Incrementing Counter...\n", x);  
  58.     counter++;  
  59.     printf("Thread %d: New Counter Value: %d\n", x, counter);  
  60.     printf("Thread %d: Exiting critical region...\n", x);  
  61.     /* END CRITICAL REGION */      
  62.     sem_post(&mutex);       /* up semaphore */  
  63.       
  64.     pthread_exit(0); /* exit thread */  
  65. }  


GitHubhttps://github.com/fengbingchun/Linux_Code_Test

猜你喜欢

转载自blog.csdn.net/wangweizhaoxin/article/details/86059380