• C- 使用原子变量实现信号量


    信号量

    信号量(Semaphore)是并发编程中的一个核心同步原语,它在多进程和多线程环境下被设计用来协调不同的执行单元,确保它们在对共享资源的访问上达到同步和互斥。信号量内部维护一个计数器,该计数器的初始值可以被视为可用资源的数量。当一个进程或线程试图“获取”一个信号量时,该计数器会递减;当它“释放”信号量时,计数器则递增。如果计数器的值达到零,任何试图获取信号量的操作都会被阻塞,直至其他进程或线程释放资源。

    信号量通常可以分为两大类:命名信号量(Named Semaphores)和无名信号量(Unnamed Semaphores)。命名信号量是通过一个独特的标识符,在系统级别进行识别的,通常与文件系统上的某个文件关联。这使得不同的进程可以通过这一标识符来定位和操作同一个信号量。而无名信号量主要存在于进程的内存地址空间中,通常用于进程内的线程同步。由于无名信号量仅存在于进程的内存中,因此它们的生命周期与包含它们的进程相同。

    为了有效利用信号量,开发者需要深入理解其工作机制和相关的API调用,确保在并发和竞争条件下实现正确、高效的资源访问控制。

    实现信号量

    实现信号量仅使用原子变量是相对复杂的。以下是一个简单的信号量实现,使用 C11 的 atomic_int

    #include 
    #include 
    #include 
    #include 
    
    typedef struct {
        atomic_int value;
    } AtomicSemaphore;
    
    void AtomicSemaphore_init(AtomicSemaphore* sem, int initial) {
        atomic_store(&sem->value, initial);
    }
    
    void AtomicSemaphore_wait(AtomicSemaphore* sem) {
        int expected;
        do {
            while ((expected = atomic_load(&sem->value)) <= 0) {
                // busy-wait/spin until value is greater than 0
            }
        } while (!atomic_compare_exchange_weak(&sem->value, &expected, expected - 1));
    }
    
    void AtomicSemaphore_post(AtomicSemaphore* sem) {
        atomic_fetch_add(&sem->value, 1);
    }
    
    void* test_func(void* arg) {
        AtomicSemaphore* sem = (AtomicSemaphore*)arg;
        AtomicSemaphore_wait(sem);
        printf("Thread %ld acquired the semaphore!\n", pthread_self());
        AtomicSemaphore_post(sem);
        return NULL;
    }
    
    int main() {
        AtomicSemaphore sem;
        AtomicSemaphore_init(&sem, 1);  // Initial value set to 1
    
        pthread_t threads[10];
        for (int i = 0; i < 10; i++) {
            pthread_create(&threads[i], NULL, test_func, &sem);
        }
    
        for (int i = 0; i < 10; i++) {
            pthread_join(threads[i], NULL);
        }
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49

    注意:

    1. 这个简单的信号量实现使用了"忙等待"或自旋,这可能会导致性能问题,尤其是在高度竞争的情况下。

    2. atomic_compare_exchange_weak 函数尝试将 sem->value 更新为 expected - 1,只有当 sem->value 的当前值与 expected 匹配时才会这样做。如果不匹配,函数将返回 false,并在 expected 中设置当前的 sem->value

    3. 在真实环境中,可能需要考虑使用更复杂的策略(例如,当信号量值为0时,线程进入休眠状态而不是持续自旋)。

    4. 虽然这个实现提供了原子操作的信号量,但它不是传统的信号量,因为它并不提供线程阻塞功能。这意味着当信号量的值为0时,线程将持续自旋,直到它可以获取信号量。

  • 相关阅读:
    【业务功能篇112】Springboot + Spring Security 权限管理-登录模块开发实战
    C#数据去重的这几种方式,你知道几种?
    mybatisPlus批量插入性能优化
    如何使用ai去水印?用这款就够了
    基于Python实现损失函数的参数估计
    opencv之VideoCapture与VideoWriter笔记
    【数据结构与算法】五、树结构-从二叉树到B+Tree
    Spring Security 中多个身份验证
    win10通过Docker搭建LNMP环境全流程
    OpenCV 的几种查找图像中轮廓边缘的方法
  • 原文地址:https://blog.csdn.net/weixin_43844521/article/details/133937143