在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:

  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. namespace {
  7. int g_Flag = 0;
  8. sem_t sem_mutex; // 用于互斥
  9. sem_t sem_syn; // 用于同步
  10. void *thread1(void *arg)
  11. {
  12. fprintf(stdout, "Enter thread1\n");
  13. fprintf(stdout, "thread1 id: %u, g_Flag: %d\n", (unsigned int)pthread_self(), g_Flag);
  14. if (sem_wait(&sem_mutex) != 0) {
  15. fprintf(stderr, "pthread1 sem_mutex fail\n");
  16. return nullptr;
  17. }
  18. if (g_Flag == 2)
  19. sem_post(&sem_syn);
  20. g_Flag = 1;
  21. if (sem_post(&sem_mutex) != 0) {
  22. fprintf(stderr, "pthread1 sem_post fail\n");
  23. return nullptr;
  24. }
  25. fprintf(stdout, "thread1 id: %u, g_Flag: %d\n", (unsigned int)pthread_self(), g_Flag);
  26. fprintf(stdout, "Leave thread1\n");
  27. pthread_t tid = pthread_self();
  28. fprintf(stdout, "thread1 tid = %u\n", tid);
  29. pthread_join(tid, nullptr);
  30. fprintf(stdout, "\n");
  31. return nullptr;
  32. }
  33. void *thread2(void *arg)
  34. {
  35. fprintf(stdout, "Enter thread2\n");
  36. fprintf(stdout, "thread2 id: %u , g_Flag: %d\n", (unsigned int)pthread_self(), g_Flag);
  37. if (sem_wait(&sem_mutex) != 0) {
  38. fprintf(stderr, "thread2 sem_wait fail\n");
  39. return nullptr;
  40. }
  41. if (g_Flag == 1)
  42. sem_post(&sem_syn);
  43. g_Flag = 2;
  44. if (sem_post(&sem_mutex) != 0) {
  45. fprintf(stderr, "thread2 sem_post fail\n");
  46. return nullptr;
  47. }
  48. fprintf(stdout, "thread2 id: %u , g_Flag: %d\n", (unsigned int)pthread_self(), g_Flag);
  49. fprintf(stdout, "Leave thread2\n");
  50. pthread_t tid = pthread_self();
  51. fprintf(stdout, "thread2 tid = %u\n", tid);
  52. pthread_join(tid, nullptr);
  53. fprintf(stdout, "\n");
  54. return nullptr;
  55. }
  56. } // namespace
  57. int main()
  58. {
  59. pthread_t tid1, tid2;
  60. sem_init(&sem_mutex, 0, 1);
  61. sem_init(&sem_syn, 0, 0);
  62. fprintf(stdout, "Inter main!\n");
  63. int ret = pthread_create(&tid2, nullptr, thread2, nullptr);
  64. if (ret != 0) {
  65. fprintf(stderr, "%s, %d\n", __func__, strerror(ret));
  66. return -1;
  67. }
  68. ret = pthread_create(&tid1, nullptr, thread1, nullptr);
  69. if (ret != 0) {
  70. fprintf(stderr, "%s, %d\n", __func__, strerror(ret));
  71. return -1;
  72. }
  73. fprintf(stdout, "Leave main!\n\n");
  74. sem_wait(&sem_syn); // 同步等待,阻塞
  75. return 0;
  76. }

test_thread_sem1.cpp:

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

test_thread_sem2.cpp:

  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. #include <unistd.h>
  7. namespace {
  8. int cnt = 0;
  9. int a[] = {1,2,3,4,5,6,7,8,9};
  10. char arr[] = {'a','b','c','d','e','f','g','h','j'};
  11. sem_t s1;
  12. void* pc(void* arg)
  13. {
  14. int i = 0;
  15. while (i < 9) {
  16. fprintf(stdout, "---- Line: %d\n", __LINE__);
  17. sem_wait(&s1);
  18. while (cnt == 0) {
  19. //fprintf(stdout, "---- Line: %d\n", __LINE__); // 注意:打开此条语句对结果的影响
  20. sem_post(&s1);
  21. }
  22. fprintf(stdout, "%c\n", arr[i++]);
  23. sleep(1);
  24. cnt=0;
  25. sem_post(&s1);
  26. }
  27. return nullptr;
  28. }
  29. void* pi(void* arg)
  30. {
  31. int i = 0;
  32. while (i < 9) {
  33. sleep(2);
  34. fprintf(stdout, "++++ Line: %d\n", __LINE__);
  35. sem_wait(&s1);
  36. while (cnt == 1) {
  37. sem_post(&s1);
  38. }
  39. fprintf(stdout, "%d\n", a[i++]);
  40. sleep(1);
  41. cnt = 1;
  42. sem_post(&s1);
  43. }
  44. return nullptr;
  45. }
  46. } // namespace
  47. int main()
  48. {
  49. pthread_t t1,t2;
  50. sem_init(&s1, 0, 1);
  51. pthread_create(&t1, nullptr, pc, nullptr);
  52. pthread_create(&t2, nullptr, pi, nullptr);
  53. pthread_join(t1, nullptr);
  54. pthread_join(t2, nullptr);
  55. sem_destroy(&s1);
  56. return 0;
  57. }

test_thread_sem3.cpp:

  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. namespace {
  12. /* global vars */
  13. /* semaphores are declared global so they can be accessed
  14. in main() and in thread routine,
  15. here, the semaphore is used as a mutex */
  16. sem_t mutex;
  17. int counter = 0; /* shared variable */
  18. /* prototype for thread routine */
  19. void* handler(void* ptr)
  20. {
  21. int x;
  22. x = *((int *)ptr);
  23. printf("Thread %d: Waiting to enter critical region...\n", x);
  24. sem_wait(&mutex); /* down semaphore */
  25. /* START CRITICAL REGION */
  26. printf("Thread %d: Now in critical region...\n", x);
  27. printf("Thread %d: Counter Value: %d\n", x, counter);
  28. printf("Thread %d: Incrementing Counter...\n", x);
  29. counter++;
  30. printf("Thread %d: New Counter Value: %d\n", x, counter);
  31. printf("Thread %d: Exiting critical region...\n", x);
  32. /* END CRITICAL REGION */
  33. sem_post(&mutex); /* up semaphore */
  34. pthread_exit(0); /* exit thread */
  35. }
  36. } // namespace
  37. int main()
  38. {
  39. int i[2];
  40. pthread_t thread_a;
  41. pthread_t thread_b;
  42. i[0] = 0; /* argument to threads */
  43. i[1] = 1;
  44. sem_init(&mutex, 0, 1); /* initialize mutex to 1 - binary semaphore */
  45. /* second param = 0 - semaphore is local */
  46. /* Note: you can check if thread has been successfully created by checking return value of
  47. pthread_create */
  48. pthread_create(&thread_a, NULL, handler, (void*)&i[0]);
  49. pthread_create(&thread_b, NULL, handler, (void*)&i[1]);
  50. pthread_join(thread_a, NULL);
  51. pthread_join(thread_b, NULL);
  52. sem_destroy(&mutex); /* destroy semaphore */
  53. /* exit */
  54. exit(0);
  55. } /* main() */

GitHub: https://github.com/fengbingchun/Linux_Code_Test

转载自:https://blog.csdn.net/fengbingchun/article/details/54178106

Multi_thread--Linux下多线程编程中信号量介绍及简单使用相关推荐

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

    在Linux中有两种方法用于处理线程同步:信号量和互斥量. 线程的信号量是一种特殊的变量,它可以被增加或减少,但对其的关键访问被保证是原子操作.如果一个程序中有多个线程试图改变一个信号量的值,系统将保 ...

  2. [原创]手把手教你Linux下的多线程设计--Linux下多线程编程详解(一)

    本文可任意转载,但必须注明作者和出处. [原创]手把手教你Linux下的多线程设计(一)                                       --Linux下多线程编程详解 原 ...

  3. Linux下高性能网络编程中的几个TCP/IP选项

    Linux下高性能网络编程中的几个TCP/IP选项 转自:http://blog.chinaunix.net/u/12592/showart.php?id=2064847 最近在新的平台上测试程序,以 ...

  4. linux 多线程 semaphore ,Linux下多线程编程-Pthread和Semaphore使用.doc

    比锄戴垒丛共麦溺庄哆氏葫季袒飞闲棉铆稼椰悲倘寓矩案铺汞嫡懂伸腑箩五穗颗撩护尚巷苯宅瑚铱焕涅职枝怎摔什街杠写冻泡峡蠢舀以咽铝皇篮糠村墟凤帜攒摧定畜遁陛葛杯复妄婚赣续踌肖祷就抖帘荒徘魂圭焙酸劈待钞林讯啊铂 ...

  5. Linux下多线程编程

    线程(thread)技术早在60年代就被提出,但真正应用多线程到操作系统中去,是在80年代中期,solaris是这方面的佼佼者.传统的Unix也支持线程的概念,但是在一个进程(process)中只允许 ...

  6. Linux下多线程编程互斥锁和条件变量的简单使用

    Linux下的多线程遵循POSIX线程接口,称为pthread.编写Linux下的多线程程序,需要使用头文件pthread.h,链接时需要使用库libpthread.a.线程是进程的一个实体,是CPU ...

  7. linux下socket编程中setsockopt的作用

    如题所示,在linux进行socket编程的时候,一般而言,socket,bind,listen三步曲之后,就开始接收客户端请求,然后实现收发数据. 如下所示的代码,是没有setsockopt的情况: ...

  8. Linux下Python3编程中shebang的正确设置

    Linux下一般情况下输入python或者python2会出现python2.7的IDLE,输入python3会出现python3.x的交互式界面,这些都是以软链接的形式存在于PATH中的,但是我们的 ...

  9. Linux下高性能网络编程中的几个TCP/IP选项_SO_REUSEADDR、SO_RECVBUF、SO_SNDBUF、SO_KEEPALIVE、SO_LINGER、TCP_CORK、TCP_NODE

    最近在新的平台上测试程序,以前一些没有注意到的问题都成为了性能瓶颈,通过设置一些TCP/IP选项能够解决一部分问题,当然根本的解决方法是重构代码,重新设计服务器框架.先列出几个TCP/IP选项: 选项 ...

最新文章

  1. ASP.NET 3.5揭秘-读书笔记1
  2. Infragistics NetAdvantage 2006 Volume 2 CLR 2.0曲折安装
  3. wallfall瀑布流的jq实现
  4. 【视频】云信CTO阙杭宁:IM云开发经验分享
  5. 人工智能化发展已经到了哪一步?
  6. vs怎么调试php程序,vscode如何调试运行c#程序
  7. JS+Selenium+excel追加写入,使用python成功爬取京东任何商品
  8. 纽交所决定将蛋壳公寓ADS摘牌
  9. jq动态拼接html页面及数据
  10. IM 产品设计思考(4)- 问答机器人
  11. ThreadLocal工具类
  12. 那个男人他不装了,他必须硬卷!
  13. LibreOJ 2060 食物链
  14. 【愚公系列】2023年05月 攻防世界-MOBILE(Phishing is not a crime-2)
  15. 互联网怎么赚钱 by taosay --集结贴
  16. 全球与中国机器人随机装箱机市场深度研究分析报告
  17. office2010打开excel文档时为空白的解决方法
  18. Python 海龟绘图 100 题——第 72 题
  19. 苹果手机点击输入框时页面自动放大
  20. request+python : shuold be true判等的问题

热门文章

  1. Mysql中bigint、int、mediumint、smallint 和 tinyint的取值范围
  2. Spring验证示例 - Spring MVC Form Validator
  3. Lucene 和 ElasticSearch 的前世今生
  4. [watchtower] 自动更新 Docker 镜像与容器
  5. CCF 201609-2 火车购票
  6. Git 的安装及配置
  7. 常用算法以及加密工具
  8. ios react_查找内存泄漏React本机应用程序(iOS)
  9. java map reduce 原理_MapReduce实现原理详解
  10. go redis 清空所有值_【大厂面试】面试官看了赞不绝口的Redis笔记二