【Linux线程同步专题】四、信号量

简介: 【Linux线程同步专题】四、信号量

1. 什么是信号量

信号量相当于进化版的互斥锁。由于互斥锁的粒度比较大,如果我们希望在多个线程间对某一对象的部分数据进行共享,使用互斥锁是没有办法实现的,只能将整个数据对象锁住。这样虽然达到了多线程操作共享数据时保证数据正确性的目的,却无形中导致线程的并发性下降,线程从并行执行变成了串行执行,与直接使用单进程无异。信号量是相对折衷的一种处理方式,既能保证同步,数据不混乱,又能提高线程并发。

可以这么理解,比如说现在有一个资源可以允许三个线程同时访问,如果用互斥量的话,当第一个线程获取互斥锁的时候,后面的线程都会阻塞,这就无法实现三个线程同时访问资源了,会大大降低资源的利用效率。如果使用信号量,那个给信号量一个初值,每有一个线程访问到资源,信号量就减一,当减到0的时候,说明已经满足最多同时访问的线程数量了,后面的线程就不能再访问资源了,会阻塞。

2. 信号量相关API

2.1 初始化一个信号量

  • 头文件及函数原型
#include <semaphore.h>
int sem_init(sem_t *sem, int pshared, unsigned int value);
Link with -lrt or -pthread.
  • 函数描述
    sem_init() initializes the unnamed semaphore at the address pointed to by sem. The value argument specifies the initial value for the semaphore.
  • 函数参数
  • sem:传输参数,代表信号量,不能小于0。sem_t信号量数据类型,是一个结构体,可以简单理解为类似于文件描述符的东西。sem_t数据类型的实现是对用户隐藏的,所以在后面的++和–操作都是只能通过函数sem_wait和sem_post来实现。信号量的初值决定了占用信号量的线程的个数。
  • pshared:
  • 0:用于线程间
  • 非0:用于进程间
  • value:指定信号量初值
  • 函数返回值
    sem_init() returns 0 on success; on error, -1 is returned, and errno is set to indicate the error.

2.2 销毁一个信号量

  • 头文件及函数原型
#include <semaphore.h>
int sem_destroy(sem_t *sem);
Link with -lrt or -pthread.
  • 函数描述
    sem_destroy() destroys the unnamed semaphore at the address pointed to by sem.
  • 函数参数
  • sem
  • 函数返回值
    sem_destroy() returns 0 on success; on error, -1 is returned, and errno is set to indicate the error.

2.3 申请一个信号量(申请成功value–)

  • 头文件及函数原型
#include <semaphore.h>
/*--操作*/
int sem_wait(sem_t *sem);
int sem_trywait(sem_t *sem);
int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);
Link with -lrt or -pthread.
  • 函数描述
  • sem_wait() decrements (locks) the semaphore pointed to by sem. 信号量大于0,则信号量–,信号量等于0,则线程阻塞。
  • sem_trywait() is the same as sem_wait(), except that if the decrement can not be immediately performed, then call returns an error (errno set to EAGAIN) instead of blocking.
  • sem_timedwait() is the same as sem_wait(), except that abs_timeout specifies a limit on the amount of time that the call should block if the decrement cannot be immediately performed.
  • 函数参数
  • sem
  • 函数返回值
    All of these functions return 0 on success; on error, the value of the semaphore is left unchanged, -1 is returned, and errno is set to indicate the error.

2.4 释放信号量(value++)

  • 头文件及函数原型
#include <semaphore.h>
/*++操作*/
int sem_post(sem_t *sem);
Link with -lrt or -pthread.
  • 函数描述
    sem_post() increments (unlocks) the semaphore pointed to by sem. 信号量++,同时唤醒阻塞在信号量上的线程。
  • 函数参数
  • sem
  • 函数返回值
    sem_post() returns 0 on success; on error, the value of the semaphore is left unchanged, -1 is returned, and errno is set to indicate the error.

3. 信号量实现生产者消费者模型

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>
#define MAX 6
sem_t blank, ful;
int vector[MAX];
int gdata = 100;
void* producer_th(void* arg)
{
    int i = 0;
    while(1)
    {
        sem_wait(&blank);
        printf("thread: %s, tid: %lu, data: %d\n", __FUNCTION__,\
               pthread_self(), gdata);
        vector[(i++)%MAX] = gdata++;
        sem_post(&ful);
        sleep(rand()%3);
    }
    return NULL;
}
void* consumer_th(void* arg)
{
    int i = 0;
    int num = 0;
    while(1)
    {
        sem_wait(&ful);
        num = vector[(i++) % MAX];
        printf("thread: %s, tid: %lu, data: %d\n", __FUNCTION__,\
               pthread_self(), num);
        sem_post(&blank);
        sleep(rand()%3);
    }
    return NULL;
}
int main(int argc, char* argv[])
{
    sem_init(&blank, 0, MAX); /*生产者*/
    sem_init(&ful, 0, 0); /*消费者*/
    pthread_t t1, t2;
    pthread_create(&t1, NULL, producer_th, NULL);
    pthread_create(&t2, NULL, consumer_th, NULL);
    pthread_join(t1, NULL);
    pthread_join(t2, NULL);
    sem_destroy(&blank);
    sem_destroy(&ful);
    return 0;
}


相关文章
|
1月前
|
资源调度 Linux 调度
Linux C/C++之线程基础
这篇文章详细介绍了Linux下C/C++线程的基本概念、创建和管理线程的方法,以及线程同步的各种机制,并通过实例代码展示了线程同步技术的应用。
29 0
Linux C/C++之线程基础
|
1月前
|
运维 API 计算机视觉
深度解密协程锁、信号量以及线程锁的实现原理
深度解密协程锁、信号量以及线程锁的实现原理
34 1
|
1月前
|
安全 Linux
Linux线程(十一)线程互斥锁-条件变量详解
Linux线程(十一)线程互斥锁-条件变量详解
|
2月前
|
Java 数据中心 微服务
Java高级知识:线程池隔离与信号量隔离的实战应用
在Java并发编程中,线程池隔离与信号量隔离是两种常用的资源隔离技术,它们在提高系统稳定性、防止系统过载方面发挥着重要作用。
44 0
|
3月前
|
消息中间件 Linux 开发者
Linux进程间通信秘籍:管道、消息队列、信号量,一文让你彻底解锁!
【8月更文挑战第25天】本文概述了Linux系统中常用的五种进程间通信(IPC)模式:管道、消息队列、信号量、共享内存与套接字。通过示例代码展示了每种模式的应用场景。了解这些IPC机制及其特点有助于开发者根据具体需求选择合适的通信方式,促进多进程间的高效协作。
161 3
|
3月前
|
安全 C++
利用信号量实现线程顺序执行
【8月更文挑战第25天】信号量是多线程编程中用于控制共享资源访问的关键同步机制,能有效保证线程按预设顺序执行。实现方法包括:引入相关头文件(如 C++ 中的 `&lt;semaphore.h&gt;`),创建信号量并通过 `sem_init` 设置初始值;在各线程函数中运用 `sem_post` 与 `sem_wait` 来传递执行权;最后,通过 `sem_destroy` 销毁信号量以释放资源。使用过程中需注意错误处理、确保线程安全及合理设定信号量初值,以维持程序稳定性和高效性。
|
3月前
利用信号量实现线程顺序执行
【8月更文挑战第24天】本文介绍了如何运用信号量确保多线程程序中线程按预定顺序执行的方法。信号量作为同步机制,可有效控制共享资源访问,防止数据不一致。实现步骤包括:引入必要的头文件(如 `&lt;pthread.h&gt;` 和 `&lt;semaphore.h&gt;`),定义信号量变量(如 `sem_t` 类型),初始化信号量(通常第一个信号量设为1,其余设为0),以及创建线程(每个线程执行特定任务并释放相应信号量)。
|
3月前
|
存储 设计模式 NoSQL
Linux线程详解
Linux线程详解
|
2月前
|
Linux
linux内核 —— 读写信号量实验
linux内核 —— 读写信号量实验
|
3月前
|
开发者 API Windows
从怀旧到革新:看WinForms如何在保持向后兼容性的前提下,借助.NET新平台的力量实现自我进化与应用现代化,让经典桌面应用焕发第二春——我们的WinForms应用转型之路深度剖析
【8月更文挑战第31天】在Windows桌面应用开发中,Windows Forms(WinForms)依然是许多开发者的首选。尽管.NET Framework已演进至.NET 5 及更高版本,WinForms 仍作为核心组件保留,支持现有代码库的同时引入新特性。开发者可将项目迁移至.NET Core,享受性能提升和跨平台能力。迁移时需注意API变更,确保应用平稳过渡。通过自定义样式或第三方控件库,还可增强视觉效果。结合.NET新功能,WinForms 应用不仅能延续既有投资,还能焕发新生。 示例代码展示了如何在.NET Core中创建包含按钮和标签的基本窗口,实现简单的用户交互。
67 0