导读:本期聚焦于小伙伴创作的《Linux中什么是临界区,临界区有什么作用和实现方式》,敬请观看详情,探索知识的价值。以下视频、文章将为您系统阐述其核心内容与价值。如果您觉得《Linux中什么是临界区,临界区有什么作用和实现方式》有用,将其分享出去将是对创作者最好的鼓励。

在Linux多进程或多线程的并发编程场景中,多个执行单元可能会同时操作同一块共享资源,比如全局变量、共享内存、硬件设备等,这种并发访问很容易导致数据错乱、程序逻辑异常等问题,而临界区就是解决这类问题的核心概念。

Linux中什么是临界区,临界区有什么作用和实现方式

临界区的定义

临界区(Critical Section)指的是一段需要访问共享资源的代码段,这段代码的执行有一个核心约束:同一时间只能有一个进程或线程进入并执行,其他想要进入的执行单元必须等待当前执行单元退出临界区之后才能进入。

这里的共享资源可以是全局变量、共享内存段、打开的同一个文件、网络套接字等,只要是被多个执行单元共同使用的资源,访问它的相关代码就需要被纳入临界区管理。

临界区的核心作用

临界区的存在主要是为了解决并发场景下的资源竞争问题,具体作用可以分为两点:

  • 保证共享资源访问的互斥性,同一时间只有一个执行单元操作共享资源,避免出现数据覆写、读取脏数据等问题。
  • 保证共享资源操作的原子性,临界区内的代码要么全部执行完成,要么完全不执行,不会被其他执行单元打断导致逻辑拆分。

Linux中临界区的常见实现方式

1. 互斥锁实现临界区

互斥锁(Mutex)是Linux中最常用的临界区实现方式,它通过加锁和解锁的操作,保证同一时间只有一个线程能进入临界区。以下是使用POSIX互斥锁实现临界区的示例代码:

#include <stdio.h>
#include <pthread.h>

// 定义共享资源
int shared_num = 0;
// 定义互斥锁
pthread_mutex_t mutex;

// 线程执行函数
void* thread_func(void* arg) {
    int i;
    for (i = 0; i < 10000; i++) {
        // 进入临界区前加锁
        pthread_mutex_lock(&mutex);
        // 临界区开始:操作共享资源
        shared_num++;
        // 临界区结束:解锁
        pthread_mutex_unlock(&mutex);
    }
    return NULL;
}

int main() {
    pthread_t t1, t2;
    // 初始化互斥锁
    pthread_mutex_init(&mutex, NULL);
    // 创建两个线程
    pthread_create(&t1, NULL, thread_func, NULL);
    pthread_create(&t2, NULL, thread_func, NULL);
    // 等待线程执行完成
    pthread_join(t1, NULL);
    pthread_join(t2, NULL);
    // 销毁互斥锁
    pthread_mutex_destroy(&mutex);
    printf("最终共享资源的值:%dn", shared_num);
    return 0;
}

上述代码中,pthread_mutex_lockpthread_mutex_unlock之间的代码就是临界区,两个线程同一时间只能有一个进入该区域操作shared_num,最终输出结果会是20000,不会出现数据错误。

2. 信号量实现临界区

信号量也可以用来实现临界区,当信号量的初始值设为1时,它的功能和互斥锁类似,同样能保证同一时间只有一个执行单元进入临界区。以下是使用POSIX信号量实现临界区的示例:

#include <stdio.h>
#include <pthread.h>
#include <semaphore.h>

// 定义共享资源
int shared_count = 0;
// 定义信号量
sem_t sem;

// 线程执行函数
void* thread_work(void* arg) {
    int i;
    for (i = 0; i < 5000; i++) {
        // 申请信号量,相当于进入临界区前加锁
        sem_wait(&sem);
        // 临界区:操作共享资源
        shared_count += 2;
        // 释放信号量,相当于退出临界区解锁
        sem_post(&sem);
    }
    return NULL;
}

int main() {
    pthread_t tid1, tid2;
    // 初始化信号量,初始值为1
    sem_init(&sem, 0, 1);
    // 创建两个线程
    pthread_create(&tid1, NULL, thread_work, NULL);
    pthread_create(&tid2, NULL, thread_work, NULL);
    // 等待线程结束
    pthread_join(tid1, NULL);
    pthread_join(tid2, NULL);
    // 销毁信号量
    sem_destroy(&sem);
    printf("最终共享计数:%dn", shared_count);
    return 0;
}

3. 原子操作实现简单临界区

如果临界区的操作只是简单的变量加减、位运算等,也可以使用Linux提供的原子操作接口来实现,不需要加锁解锁的开销。以下是使用GCC内置原子操作实现临界区的示例:

#include <stdio.h>
#include <pthread.h>

// 定义原子类型的共享变量
volatile int atomic_val = 0;

// 线程执行函数
void* atomic_thread(void* arg) {
    int i;
    for (i = 0; i < 10000; i++) {
        // 原子自增操作,本身就是临界区,不需要额外加锁
        __sync_fetch_and_add(&atomic_val, 1);
    }
    return NULL;
}

int main() {
    pthread_t t1, t2;
    pthread_create(&t1, NULL, atomic_thread, NULL);
    pthread_create(&t2, NULL, atomic_thread, NULL);
    pthread_join(t1, NULL);
    pthread_join(t2, NULL);
    printf("原子操作最终值:%dn", atomic_val);
    return 0;
}

临界区使用的注意事项

  • 临界区的代码要尽量简短,只把访问共享资源的必要代码放进去,减少其他执行单元的等待时间,提升并发性能。
  • 进入临界区加锁之后,一定要保证在所有的执行路径(包括异常分支)都能正确解锁,否则会导致死锁问题。
  • 不要在临界区内调用可能阻塞的函数,比如sleep、长时间等待IO等,会导致其他执行单元长时间无法进入临界区。
  • 如果多个临界区访问的是不同的共享资源,不要使用同一把锁,否则会降低并发度,应该使用不同的锁分别管理。

Linux临界区critical_section进程同步互斥锁修改时间:2026-06-21 08:06:28

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。