Linux多线程——使用信号量同步线程

 

一、什么是信号量

线程的信号量与进程间通信中使用的信号量的概念是一样,它是一种特殊的变量,它可以被增加或减少,但对其的关键访问被保证是原子操作。如果一个程序中有多个线程试图改变一个信号量的值,系统将保证所有的操作都将依次进行。

 

而只有0和1两种取值的信号量叫做二进制信号量,在这里将重点介绍。而信号量一般常用于保护一段代码,使其每次只被一个执行线程运行。我们可以使用二进制信号量来完成这个工作。

 

二、信号量的接口和使用

 

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

 

1、sem_init函数

该函数用于创建信号量,其原型如下:

  1. int sem_init(sem_t *sem, int pshared, unsigned int value);  

该函数初始化由sem指向的信号对象,设置它的共享选项,并给它一个初始的整数值。pshared控制信号量的类型,如果其值为0,就表示这个信号量是当前进程的局部信号量,否则信号量就可以在多个进程之间共享,value为sem的初始值。调用成功时返回0,失败返回-1.

 

2、sem_wait函数

该函数用于以原子操作的方式将信号量的值减1。原子操作就是,如果两个线程企图同时给一个信号量加1或减1,它们之间不会互相干扰。它的原型如下:

  1. int sem_wait(sem_t *sem);  

sem指向的对象是由sem_init调用初始化的信号量。调用成功时返回0,失败返回-1.

 

3、sem_post函数

该函数用于以原子操作的方式将信号量的值加1。它的原型如下:

  1. int sem_post(sem_t *sem);  

与sem_wait一样,sem指向的对象是由sem_init调用初始化的信号量。调用成功时返回0,失败返回-1.

 

4、sem_destroy函数

该函数用于对用完的信号量的清理。它的原型如下:

 

  1. int sem_destroy(sem_t *sem);  

成功时返回0,失败时返回-1.

 

三、使用信号量同步线程

 

下面以一个简单的多线程程序来说明如何使用信号量进行线程同步。在主线程中,我们创建子线程,并把数组msg作为参数传递给子线程,然后主线程等待直到有文本输入,然后调用sem_post来增加信号量的值,这样就会立刻使子线程从sem_wait的等待中返回并开始执行。线程函数在把字符串的小写字母变成大写并统计输入的字符数量之后,它再次调用sem_wait并再次被阻塞,直到主线程再次调用sem_post增加信号量的值。

  1. #include <unistd.h>  
  2. #include <pthread.h>  
  3. #include <semaphore.h>  
  4. #include <stdlib.h>  
  5. #include <stdio.h>  
  6. #include <string.h>  
  7.   
  8. //线程函数  
  9. void *thread_func(void *msg);  
  10. sem_t sem;//信号量  
  11.   
  12. #define MSG_SIZE 512  
  13.   
  14. int main()  
  15. {  
  16.     int res = -1;  
  17.     pthread_t thread;  
  18.     void *thread_result = NULL;  
  19.     char msg[MSG_SIZE];  
  20.     //初始化信号量,其初值为0  
  21.     res = sem_init(&sem, 0, 0);  
  22.     if(res == -1)  
  23.     {  
  24.         perror("semaphore intitialization failed\n");  
  25.         exit(EXIT_FAILURE);  
  26.     }  
  27.     //创建线程,并把msg作为线程函数的参数  
  28.     res = pthread_create(&thread, NULL, thread_func, msg);  
  29.     if(res != 0)  
  30.     {  
  31.         perror("pthread_create failed\n");  
  32.         exit(EXIT_FAILURE);  
  33.     }  
  34.     //输入信息,以输入end结束,由于fgets会把回车(\n)也读入,所以判断时就变成了“end\n”  
  35.     printf("Input some text. Enter 'end'to finish...\n");  
  36.     while(strcmp("end\n", msg) != 0)  
  37.     {  
  38.         fgets(msg, MSG_SIZE, stdin);  
  39.         //把信号量加1  
  40.         sem_post(&sem);  
  41.     }  
  42.   
  43.     printf("Waiting for thread to finish...\n");  
  44.     //等待子线程结束  
  45.     res = pthread_join(thread, &thread_result);  
  46.     if(res != 0)  
  47.     {  
  48.         perror("pthread_join failed\n");  
  49.         exit(EXIT_FAILURE);  
  50.     }  
  51.     printf("Thread joined\n");  
  52.     //清理信号量  
  53.     sem_destroy(&sem);  
  54.     exit(EXIT_SUCCESS);  
  55. }  
  56.   
  57. void* thread_func(void *msg)  
  58. {  
  59.     //把信号量减1  
  60.     sem_wait(&sem);  
  61.     char *ptr = msg;  
  62.     while(strcmp("end\n", msg) != 0)  
  63.     {  
  64.         int i = 0;  
  65.         //把小写字母变成大写  
  66.         for(; ptr[i] != '\0'; ++i)  
  67.         {  
  68.             if(ptr[i] >= 'a' && ptr[i] <= 'z')  
  69.             {  
  70.                 ptr[i] -= 'a' - 'A';  
  71.             }  
  72.         }  
  73.         printf("You input %d characters\n", i-1);  
  74.         printf("To Uppercase: %s\n", ptr);  
  75.         //把信号量减1  
  76.         sem_wait(&sem);  
  77.     }  
  78.     //退出线程  
  79.     pthread_exit(NULL);  
  80. }  

源码地址获取mingli.com

有兴趣的朋友们可以前往球球哦~一起分享学习技术:2042849237

转载于:https://my.oschina.net/u/3451464/blog/900206

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
目录 1 进程的组织 5 1.1 进程相关数据结构 5 1.1.1 进程的基本信息 6 1.1.2 进程状态 10 1.1.3 TASK_RUNNING状态的进程链表 11 1.1.4 进程间关系 12 1.2 Linux线程——轻量级进程 15 1.3 进程的创建——do_fork()函数详解 19 1.4 执行进程间切换 33 1.4.1 进程切换之前的工作 33 1.4.2 进程切换实务 —— switch_to宏 37 1.4.3 __switch_to函数 39 1.5 fork与vfock系统调用的区别 42 1.6 内核线程 46 1.7 挂起状态进程的组织 49 1.7.1 等待队列头 49 1.7.2 等待队列的操作 50 1.7.3 进程资源限制 55 1.8 系统调用execve() 56 1.8.1 拷贝用户态参数 57 1.8.2 重要的数据结构 61 1.8.3 search_binary_handler函数 66 1.8.4 目标文件的装载和投入运行 69 1.8.5 库函数 92 2 中断控制 94 2.1 中断的分类 94 2.2 中断的硬件环境 95 2.2.1 外部中断请求IRQ 95 2.2.2 中断描述符表 96 2.2.3 中断和异常的硬件处理 97 2.3 中断描述符表 99 2.3.1 中断门、陷阱门及系统门 99 2.3.2 IDT的初步初始化 100 2.4 异常处理 101 2.5 中断处理 106 2.5.1 中断向量 107 2.5.2 IRQ数据结构 108 2.5.3 do_IRQ()函数 113 2.5.4 中断服务例程 115 2.5.5 IRQ线的动态分配 116 2.6 下半部分 117 2.6.1 软中断 118 2.6.2 tasklet 121 2.6.3 工作队列 122 2.7定时器中断 124 2.7.1 时钟与定时器 124 2.7.2 定时器中断相关的数据结构 127 2.7.3 定时器中断的上半部分 129 3 进程调度 138 3.1 进程调度的概念 138 3.2 进程调度的数据结构和优先级 141 3.2.1 进程的优先级 141 3.2.2 数据结构 145 3.3 调度程序所使用的函数 151 3.3.1 scheduler_tick函数 151 3.3.2 try_to_wake_up函数 156 3.3.3 recalc_task_prio函数 160 3.4 schedule()函数 163 3.4.1 直接调用 163 3.4.2 延迟调用 164 3.4.3 进程切换之前所做的工作 168 3.4.4 完成进程切换时所执行的操作 171 3.4.5 进程切换后所执行的操作 173 3.5 多处理器运行队列的平衡 175 3.5.1 调度域 176 3.5.2 rebalance_tick()函数 178 3.5.3 load_balance()函数 180 3.5.4 move_tasks()函数 183 3.6 进程退出 187 3.6.1 进程终止 187 3.6.2 进程删除 189 4 进程的并发性体现 191 4.1 内核抢占 193 4.1.1 内核抢占概念 193 4.1.2 同步技术总揽 196 4.2 每CPU变量 197 4.3 原子操作 199 4.4 优化屏障和内存壁垒 203 4.4.1 优化屏障 204 4.4.2 内存壁垒 204 4.5 自旋锁 206 4.6 读写自旋锁 211 4.6.1 为读获取和释放一个锁 213 4.6.2 为写获取或释放一个锁 214 4.7 顺序锁 215 4.8 RCU机制 217 4.9 信号量 219 4.9.1 获取和释放信号量 221 4.9.2 读/写信号量 224 4.9.3 补充信号量 225 4.10 禁止本地中断 226 4.10.1 禁止本地中断 227 4.10.2 禁止下半部(可延迟函数) 229 4.11 一些避免竞争条件的实例 231 4.11.1 引用计数器 231 4.11.2 大内核锁 231 4.11.3 内存描述符读/写信号量 232 4.11.4 slab高速缓存链表的信号量 233 4.11.5 索引节点的信号量 233 4.12 内核同步与互斥的总结 233
### 回答1: Linux多线程信号量是一种用于控制多个线程并发访问共享资源的机制。它可以确保同一时间只有一个线程能够访问共享资源,从而避免了多个线程同时访问共享资源而导致的数据竞争和不一致性问题。在Linux中,信号量通常使用semaphore机制来实现,它提供了一组原子操作,包括P操作和V操作,用于对信号量进行加锁和解锁操作。通过使用信号量,可以有效地保证多线程程序的正确性和可靠性。 ### 回答2: Linux多线程信号量是一种保护机制,用于控制多个线程同时访问共享资源的方式。在Linux环境下,信号量一般指的是二进制信号量或计数信号量。 二进制信号量可以看做是一个锁,线程在访问共享资源时必须获得这个锁,否则就会被阻塞等待锁释放。在Linux中,使用sem_init()函数创建二进制信号量,sem_wait()函数获取锁,sem_post()函数释放锁。在获取锁时,如果锁已被占用,则该线程会被阻塞,直到锁释放。 计数信号量可以表示一类资源的数量,线程使用一定数量的资源时需要获取相应数量的计数信号量。在Linux中,使用sem_init()函数创建计数信号量,sem_wait()函数获取信号量,sem_post()函数释放信号量。在获取计数信号量时,如果当前数量不足,则该线程会被阻塞,直到资源数量足够。 Linux多线程信号量使用可以避免多个线程同时对共享资源操作的问题,提高系统并发度和稳定性。但是需要注意的是,使用信号量并不能解决所有并发问题,还需要结合其他同步机制,如互斥锁、条件变量等,才能构建完整的并发控制方案。 ### 回答3: Linux多线程信号量是一种线程同步和互斥的机制,它是由Linux内核提供的一种机制,用于控制多个线程并发访问共享资源。信号量多线程编程中被广泛使用,用于控制线程的访问和操作共享资源的顺序。 信号量的值表示着可以同时访问该共享资源的线程数量,当某个线程想要访问资源时,需要先获取信号量的锁,如果此时信号量的值为0则线程需要等待,直到其他线程释放信号量的锁后才能获得锁进入临界区。 Linux中实现了两种类型的信号量:二元信号量和计数信号量。二元信号量只有两种状态,可以表示互斥访问,即同一时间只有一个线程能访问该资源;计数信号量可以表示多个线程同时访问该资源。 在多线程编程中,线程之间的访问共享资源会涉及到许多同步和互斥的问题,使用信号量可以很好的解决这些问题。当多个线程需要资源时,信号量可以对线程进行调度,当有资源可用时不会浪费时间,同时避免了过度竞争。 使用信号量还可以避免出现死锁的情况,当多个线程同时申请资源时,如果无法获取到锁,就会进入等待状态,当某个线程获取到了锁就会释放资源,其他线程就能继续执行,从而避免了死锁的情况。 总之,Linux多线程信号量线程同步和互斥的重要机制,可以解决线程之间的竞争问题,保证程序运行的正确性和正常性,使多线程编程变得更加高效、安全、可靠。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值