一、基础概念:进程与线程

1.1 什么是进程?

进程是操作系统进行资源分配和调度的基本单位,是一个正在运行的程序实例。

1.2 什么是线程?

线程是操作系统进行CPU调度的基本单位,是进程内部的一条执行路径(轻量级进程)。

1.3 经典类比

  • 进程 = 一间房子(独立空间、水电)

  • 线程 = 房子里的多个工人(共享房间环境,各自干活)

1.4 进程与线程的核心区别

对比项 进程 线程
资源分配单位 ✅ 是 ❌ 否
CPU调度单位 ❌ 否 ✅ 是
内存空间 独立 共享进程资源
创建/切换开销
通信方式 IPC(复杂) 共享变量(简单,需同步)
崩溃影响 不影响其他进程 导致整个进程崩溃

二、核心概念:同步、异步与互斥

2.1 同步(Synchronization)

  • 定义:一个任务发起操作后,必须等待该操作完成才能继续执行。

  • 特点:阻塞、顺序、可控。

  • 例子:线程A调用read()读取文件,文件未读完前线程A被挂起。

  • 风险:若操作耗时,会导致任务卡死。

2.2 异步(Asynchronous)

  • 定义:一个任务发起操作后,无需等待操作完成,立即继续执行后续代码。

  • 特点:非阻塞、并发、效率高。

  • 例子:Node.js异步I/O,发起网络请求后立刻执行下一行代码。

  • 风险:逻辑复杂(回调地狱)、状态管理难。

2.3 互斥(Mutual Exclusion)

  • 定义:防止多个任务同时访问同一共享资源,保证资源的完整性。

  • 特点:一次只允许一个任务进入临界区。

  • 例子:两个线程同时修改全局变量,通过加锁防止数据错乱。

2.4 同步 vs 互斥 —— 一句话区分

维度 同步 互斥
核心问题 “能不能走?”(协调顺序) “能不能进?”(保护资源)
典型场景 生产者没生产完,消费者不能取 两个线程同时修改全局变量
类比 接力棒(跑完第一棒才能跑第二棒) 厕所门锁(一次进一个人)

注意:在并发编程中,“同步”还有另一层含义——指控制多个任务对共享资源的访问顺序(即互斥+同步的总称)。这与“同步操作 vs 异步操作”中的“同步”含义不同,需根据上下文区分。

三、通用OS中的同步与异步

通用OS:Windows、Linux、macOS、Android/iOS等,强调功能全面和用户体验。

3.1 同步机制(通用OS)

机制 说明 典型API
互斥量 保护共享资源,防止并发访问 pthread_mutex_lock/unlock
信号量 控制资源访问数量或任务同步 sem_wait/sem_post
条件变量 等待某个条件满足后再继续 pthread_cond_wait/signal
读写锁 允许多读单写 pthread_rwlock_rdlock/wrlock

3.2 异步机制(通用OS)

机制 说明 典型API
信号 进程间异步通知 signal()kill()
AIO 异步I/O操作 aio_readaio_error
epoll/IOCP 事件驱动,不轮询 epoll_createepoll_wait
std::async C++异步任务 std::asyncfuture.get()

四、RTOS中的同步与异步

RTOS:实时操作系统(如FreeRTOS),强调确定性和时间约束。

4.1 RTOS中的同步机制

RTOS中的同步指任务与任务之间任务与中断之间的协调机制。

机制 用途 关键特点
二值信号量 任务等待中断 无优先级继承,适合同步
计数信号量 多资源管理/生产消费 可计数
事件标志组 等待多个事件(与/或) 支持复杂触发条件
消息队列 任务间传递数据 带阻塞超时

典型同步示例

  1. 任务A调用xSemaphoreTake()阻塞,等待传感器数据

  2. 中断ISR采集完数据,调用xSemaphoreGiveFromISR()释放信号量

  3. 任务A立即解除阻塞,处理数据

4.2 RTOS中的异步机制

RTOS核心API大多是同步阻塞型(如xQueueSend可设置超时)。异步行为通常通过中断+信号量/队列模拟:

异步场景 实现方式
中断异步通知 ISR释放信号量后立即返回,不等待任务处理
任务异步消息 任务A发送消息后继续执行,不等待接收方处理
定时器回调 软件定时器回调在任务上下文中异步执行

4.3 RTOS中的“同步”与“互斥”区分

对比项 同步 互斥
推荐工具 二值信号量 互斥量
优先级继承 ❌ 不需要 ✅ 必须(防优先级翻转)
是否可在中断中使用 ✅ 可以(GiveFromISR) ❌ 不可以
典型场景 任务等待中断 保护共享资源

五、同步、异步、互斥三者的关系

概念 核心问题 典型工具(通用OS) 典型工具(RTOS)
同步 顺序协调(能不能走) 条件变量、信号量 二值信号量、事件标志组
异步 不等待结果 epoll、AIO、信号 中断+信号量/队列
互斥 资源保护(能不能进) 互斥量、读写锁 互斥量(优先级继承)

三者可以共存:一个典型的生产者-消费者模型中:

  • 互斥保护缓冲区(防止同时修改)

  • 同步协调生产者和消费者(缓冲区空时消费者等待)

  • 异步:生产者生产后不等待消费者处理,立即继续生产

六、验证方法

6.1 通用OS验证方法

同步机制验证示例(互斥量)
int counter = 0;
pthread_mutex_t mutex;
void* thread(void* arg) {
    for(int i=0; i<10000; i++) {
        pthread_mutex_lock(&mutex);
        counter++;
        pthread_mutex_unlock(&mutex);
    }
}
// 验证:加锁后结果正确(20000),不加锁结果错乱
异步机制验证示例(epoll)
int epfd = epoll_create(1);
while(1) {
    int nfds = epoll_wait(epfd, events, 10, -1);  // 阻塞等待事件
    // 验证:只有socket有数据时才返回,CPU空转为0%
    for(...) handle_event();
}
6.2 RTOS验证方法
同步机制验证示例(响应延迟测量)
volatile uint32_t enter_time, exit_time;
void ISR() {
    enter_time = DWT_CYCCNT;  // 读CPU周期计数器
    xSemaphoreGiveFromISR(sem, &woken);
    portYIELD_FROM_ISR(woken);
}
void Task() {
    xSemaphoreTake(sem, portMAX_DELAY);
    exit_time = DWT_CYCCNT;
    uint32_t latency_us = (exit_time - enter_time) / CPU_MHZ;
    // 验证:延迟应在微秒级且基本恒定
}
异步机制验证示例(ISR不等待任务)
void ISR() {
    xSemaphoreGiveFromISR(sem, NULL);  // 立即返回,不等待
    // 逻辑分析仪测量ISR出口到Task入口的时间差
}
void Task() {
    xSemaphoreTake(sem, portMAX_DELAY);
    GPIO_Toggle(LED);  // 示波器测量此引脚
}
6.3 通用OS vs RTOS验证对比
验证维度 通用OS RTOS
主要目标 正确性(无竞争、死锁) 正确性 + 实时性(延迟确定)
核心工具 软件分析工具 硬件仪器(逻辑分析仪)
关键指标 吞吐量、锁等待时间 中断延迟、响应时间、抖动

七、总结

核心概念 一句话总结
进程 资源分配的基本单位,拥有独立内存空间
线程 CPU调度的基本单位,共享进程资源
同步 等待结果再继续(阻塞)
异步 不等待结果,回头通知(非阻塞)
互斥 保护共享资源,防止同时访问
同步 vs 互斥 同步问“能不能走”,互质问“能不能进”
通用OS重点 公平性、吞吐量、功能丰富
RTOS重点 确定性、实时性、可预测延迟

八、生产者-消费者模型(多线程编程)

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
// --- 配置参数 ---
#define BUFFER_SIZE 5      // 缓冲区大小
#define NUM_ITEMS 10       // 每个生产者生产的总数量
#define PRODUCER_NUM 1     // 生产者线程数
#define CONSUMER_NUM 2     // 消费者线程数
// --- 共享资源 ---
int buffer[BUFFER_SIZE];   // 环形缓冲区
int count = 0;             // 缓冲区当前元素个数
int in = 0;                // 生产者放入数据的索引
int out = 0;               // 消费者取出数据的索引
// --- 同步原语 ---
pthread_mutex_t mutex;     // 互斥锁
pthread_cond_t not_full;   // 缓冲区未满条件变量
pthread_cond_t not_empty;  // 缓冲区非空条件变量

// --- 生产者线程函数 ---
void *producer(void *arg) {
    int id = *(int *)arg;
    int item;
    for (int i = 0; i < NUM_ITEMS; i++) {
        item = rand() % 100; // 生产一个随机数据
        // 1. 加锁
        pthread_mutex_lock(&mutex);
        // 2. 如果缓冲区满,等待
        while (count == BUFFER_SIZE) {
            pthread_cond_wait(&not_full, &mutex);
        }
        // 3. 生产数据(放入缓冲区)
        buffer[in] = item;
        in = (in + 1) % BUFFER_SIZE; // 环形索引
        count++;
        printf("生产者 %d 生产了: %d (当前库存: %d)\n", id, item, count);
        // 4. 解锁
        pthread_mutex_unlock(&mutex);
        // 5. 通知消费者(缓冲区不空了)
        pthread_cond_signal(&not_empty);
        usleep(rand() % 100000); // 模拟生产耗时
    }
    return NULL;
}

// --- 消费者线程函数 ---
void *consumer(void *arg) {
    int id = *(int *)arg;
    int item;
    // 消费者一直运行,直到手动停止或根据逻辑退出
    // 这里为了演示,简单设定一个循环次数,实际中通常由特定信号退出
    int consumed_count = 0;
    while (consumed_count < NUM_ITEMS * PRODUCER_NUM / CONSUMER_NUM + 5) {
        // 1. 加锁
        pthread_mutex_lock(&mutex);
        // 2. 如果缓冲区空,等待
        while (count == 0) {
            pthread_cond_wait(&not_empty, &mutex);
        }
        // 3. 消费数据(从缓冲区取出)
        item = buffer[out];
        out = (out + 1) % BUFFER_SIZE;
        count--;
        printf("  消费者 %d 消费了: %d (当前库存: %d)\n", id, item, count);
        // 4. 解锁
        pthread_mutex_unlock(&mutex);
        // 5. 通知生产者(缓冲区不满)
        pthread_cond_signal(&not_full);
        consumed_count++;
        usleep(rand() % 150000); // 模拟消费耗时
    }
    return NULL;
}

int main() {
    pthread_t pro_threads[PRODUCER_NUM];
    pthread_t con_threads[CONSUMER_NUM];
    int pro_ids[PRODUCER_NUM];
    int con_ids[CONSUMER_NUM];
    // 初始化互斥锁和条件变量
    pthread_mutex_init(&mutex, NULL);
    pthread_cond_init(&not_full, NULL);
    pthread_cond_init(&not_empty, NULL);
    // 创建生产者线程
    for (int i = 0; i < PRODUCER_NUM; i++) {
        pro_ids[i] = i + 1;
        pthread_create(&pro_threads[i], NULL, producer, &pro_ids[i]);
    }
    // 创建消费者线程
    for (int i = 0; i < CONSUMER_NUM; i++) {
        con_ids[i] = i + 1;
        pthread_create(&con_threads[i], NULL, consumer, &con_ids[i]);
    }
    // 等待所有生产者结束
    for (int i = 0; i < PRODUCER_NUM; i++) {
        pthread_join(pro_threads[i], NULL);
    }
    // 等待消费者结束(这里简单等待一下,实际场景可能需要更复杂的退出机制)
    sleep(2); 
    // 销毁同步对象
    pthread_mutex_destroy(&mutex);
    pthread_cond_destroy(&not_full);
    pthread_cond_destroy(&not_empty);
    printf("主线程结束。\n");
    return 0;
}

在 Linux 或 macOS 终端中,使用 gcc 编译并链接 pthread 库:

gcc producer_consumer.c -o pc_demo -lpthread
./pc_demo

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐