ToB企服应用市场:ToB评测及商务社交产业平台

标题: 【探索Linux】 P.22(POSIX信号量) [打印本页]

作者: 饭宝    时间: 2024-6-23 19:36
标题: 【探索Linux】 P.22(POSIX信号量)


  
引言

在上一篇文章中,我们深入探讨了多线程编程的焦点概念,包罗线程同步、条件变量以及线程安全等关键技能,为读者展现了并发编程的复杂性及其解决方案。这些概念和技能是实现高效、稳定并发应用程序的基础。继续在并发编程的旅途上前进,本篇文章将引导我们走进Linux操作体系下的另一个重要概念——POSIX信号量(Semaphore)。
POSIX信号量是一种用于进程或线程间同步的机制,它提供了一种控制资源访问的方法,确保在任何时候只有特定数量的线程可以访问特定的资源。这在处理资源共享问题时尤其重要,比如,在操作体系、数据库管理体系等范畴,精确的使用信号量可以有效制止死锁和竞态条件,保证体系的稳定运行。
随着并发编程的普及,掌握各种同步机制成为每位开发者的必备技能。POSIX信号量作为此中的重要组成部分,其重要性不言而喻。让我们一起深入探索POSIX信号量,解锁并发编程的新技能。
一、POSIX信号量的根本概念

POSIX信号量是一种在POSIX-compliant体系(如Linux)中实现的线程或进程间同步机制。它提供了一组标准化的API,用于控制对共享资源的访问。信号量本质上是一个计数器,用于表示可用资源的数量。它支持两个根本操作:等待(wait)和信号(signal),在差别的文献中,这两个操作也被称为P(Proberen,尝试)和V(Verhogen,增加)操作。
二、信号量的相关操作

POSIX信号量界说在<semaphore.h>头文件中,主要包罗以下几个函数
1 . 初始化信号量sem_init ( )

初始化:在使用信号量之前,必须先对其进行初始化,设定信号量的初始值,即可用资源的数量。
(1)原型

  1. int sem_init(sem_t *sem, int pshared, unsigned int value);
复制代码
(2)参数


(3)返回值


(4)示例代码

下面是一个使用sem_init初始化信号量的简朴示例:
  1. #include <stdio.h>
  2. #include <pthread.h>
  3. #include <semaphore.h>
  4. sem_t sem;
  5. void* thread_function(void* arg) {
  6.     // 等待信号量
  7.     sem_wait(&sem);
  8.     printf("Entered..\n");
  9.     // 临界区代码...
  10.     printf("Exiting..\n");
  11.     // 释放信号量
  12.     sem_post(&sem);
  13. }
  14. int main() {
  15.     // 初始化信号量,初始值设为1
  16.     if (sem_init(&sem, 0, 1) != 0) {
  17.         perror("sem_init");
  18.         return 1;
  19.     }
  20.     pthread_t t1, t2;
  21.     // 创建两个线程
  22.     pthread_create(&t1, NULL, thread_function, NULL);
  23.     pthread_create(&t2, NULL, thread_function, NULL);
  24.     // 等待线程结束
  25.     pthread_join(t1, NULL);
  26.     pthread_join(t2, NULL);
  27.     // 销毁信号量
  28.     sem_destroy(&sem);
  29.     return 0;
  30. }
复制代码
在这个示例中,我们创建了一个初始值为1的信号量,这意味着它可以被一个线程获取,从而进入临界区。当信号量的值为0时,其他试图获取该信号量的线程将会阻塞,直到信号量的值再次变为正数。通过调用sem_wait和sem_post函数,线程在进入和退出临界区时分别等待和释放信号量,从而实现了线程间的同步。
2 . 等待信号量

等待(P操作):当线程尝试获取一个资源时,会执行等待操作。如果信号量的值大于0,表示有资源可用,它就会减1并继续执行。如果信号量的值为0,表示没有可用资源,执行等待操作的线程将被阻塞,直到信号量的值变为大于0
(1)sem_wait ( )

sem_wait 函数用于等待信号量。如果信号量的值大于0,该函数会将它减1并立即返回,让调用线程继续执行。如果信号量的值为0,调用线程将阻塞,直到信号量的值变为大于0
- 原型

  1. int sem_wait(sem_t *sem);
复制代码
- 参数


- 返回值


(2)sem_trywait ( )

sem_trywait 函数尝试等待信号量,但与sem_wait差别的是,如果信号量的值为0,sem_trywait不会阻塞调用线程,而是立即返回一个错误。
- 原型

  1. int sem_trywait(sem_t *sem);
复制代码
- 参数


- 返回值


(3)sem_timedwait

sem_timedwait 函数也用于等待信号量,但它允许指定一个超时时间。如果在指定的时间内信号量没有变为可用状态,函数将返回一个错误。
- 原型

  1. int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);
复制代码
- 参数


- 返回值


(4)示例代码

下面是一个使用sem_wait等待信号量的简朴示例:
  1. #include <stdio.h>
  2. #include <pthread.h>
  3. #include <semaphore.h>
  4. sem_t sem;
  5. void* thread_function(void* arg) {
  6.     // 等待信号量
  7.     sem_wait(&sem);
  8.     printf("Entered..\n");
  9.     // 模拟临界区代码
  10.     sleep(1);
  11.     printf("Exiting..\n");
  12.     // 释放信号量
  13.     sem_post(&sem);
  14. }
  15. int main() {
  16.     // 初始化信号量,初始值设为1
  17.     if (sem_init(&sem, 0, 1) != 0) {
  18.         perror("sem_init");
  19.         return 1;
  20.     }
  21.     pthread_t t1, t2;
  22.     // 创建两个线程
  23.     pthread_create(&t1, NULL, thread_function, NULL);
  24.     pthread_create(&t2, NULL, thread_function, NULL);
  25.     // 等待线程结束
  26.     pthread_join(t1, NULL);
  27.     pthread_join(t2, NULL);
  28.     // 销毁信号量
  29.     sem_destroy(&sem);
  30.     return 0;
  31. }
复制代码
在这个示例中,sem_wait被用于确保在任何时候只有一个线程可以进入临界区执行。这是通过在进入临界区之前调用sem_wait来实现的,它会等待信号量变为可用(即信号量的值大于0)。乐成进入临界区的线程在离开时通过调用sem_post来增加信号量的值,从而大概允许其他等待中的线程进入临界区。
3 . 发布信号量sem_post( )

信号(V操作)当线程释放一个资源时,会执行信号操作。信号操作会将信号量的值加1。如果有其他线程因等待该信号量而被阻塞,此中一个线程将被叫醒,以便它可以获取资源
sem_post 函数用于增加信号量的值。当信号量的值从0变为正数时,如果有线程因调用 sem_wait 而阻塞在该信号量上,那么此中一个线程将被叫醒(即解除阻塞状态并得到信号量)。
(1)原型

  1. int sem_post(sem_t *sem);
复制代码
(2)参数


(3)返回值


(4)示例代码

以下是一个使用 sem_post 来发布(释放)信号量的简朴示例,它演示了如安在一个线程中使用 sem_post 来允许另一个线程继续执行。
  1. #include <stdio.h>
  2. #include <pthread.h>
  3. #include <semaphore.h>
  4. sem_t sem;
  5. void* thread_function(void* arg) {
  6.     printf("Thread waiting for the semaphore...\n");
  7.     sem_wait(&sem); // 等待信号量
  8.     printf("Semaphore acquired by thread.\n");
  9.     // 执行一些操作...
  10.     sleep(1); // 模拟耗时操作
  11.     printf("Thread releasing the semaphore.\n");
  12.     sem_post(&sem); // 释放信号量
  13. }
  14. int main() {
  15.     // 初始化信号量,初始值设为0
  16.     if (sem_init(&sem, 0, 0) != 0) {
  17.         perror("sem_init failed");
  18.         return 1;
  19.     }
  20.     pthread_t t1;
  21.     pthread_create(&t1, NULL, thread_function, NULL);
  22.     printf("Main thread sleeping for 2 seconds...\n");
  23.     sleep(2); // 让线程有足够的时间进入等待状态
  24.     printf("Main thread posting the semaphore.\n");
  25.     sem_post(&sem); // 主线程释放信号量,允许子线程继续执行
  26.     pthread_join(t1, NULL); // 等待子线程结束
  27.     sem_destroy(&sem); // 销毁信号量
  28.     return 0;
  29. }
复制代码
在这个示例中,主线程初始化一个信号量并创建一个工作线程,然后休眠2秒钟。工作线程启动后会尝试通过调用 sem_wait 获取信号量,但由于信号量的初始值被设置为0,所以它将被阻塞。主线程在休眠竣事后通过调用 sem_post 增加信号量的值,这导致阻塞的工作线程被叫醒并继续执行。





欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) Powered by Discuz! X3.4