69天探索操作系统-第11天:并发基础 - 竞态条件:深度解析

251 阅读3分钟

Pro3.avif

1. 并发介绍

并发是现代计算中的一个基本概念,其中多个任务或进程同时执行。在并行和分布式系统的领域,理解并发对于开发高效和可靠的软件至关重要。

image.png 关键并发特性:

  • 同时执行多个任务
  • 共享资源管理
  • 线程或进程之间的复杂交互
  • 潜在的不确定行为

2. 什么是竞态条件

当系统的行为依赖于事件相对时间时,就会发生竞态条件,特别是在并发系统中。当多个线程或进程访问共享资源时没有适当的同步,就会发生这种情况,导致无法预测的结果,可能会是错误的。

定义特性:

  • 多个线程访问共享资源
  • 缺乏适当的同步机制
  • 执行顺序可能会影响最终结果
  • 非确定性行为

3.竞态条件类型

1. 读取-修改-写入竞争条件 在此类型中,多个线程读取共享变量、修改其内容并将结果写入,可能会覆盖彼此的修改。 2. 检查-然后-行动的竞态条件 线程检查一个条件,并根据该条件采取行动,该行为可能在检查和行动之间发生变化。 3. 复合种类条件 涉及多个共享资源且线程之间存在复杂交互的复杂场景。

4.真实案例

银行交易情景

想象一下两个并发银行交易尝试同时从同一个账户中取款:

  • 线程A读取当前余额:$1000
  • 线程B读取当前余额:$1000
  • 线程A取出$500
  • 线程B取出$600
  • 实际余额与预期余额不符,显示为$400的错误余额

票务预订系统

多个用户试图预订最后一张可用票可能会导致超额预订或不一致的座位分配。

5.检测竞争条件

检测技术:

  • 静态代码分析工具
  • 动态竞态检测工具
  • 全面测试
  • 线程安全器
  • 并发工作负载下的压力测试

6. 预防策略

1. 互斥锁(互斥)

使用锁确保每次只有一个线程可以访问临界区。

2. 原子操作

利用编程语言和硬件提供的原子指令。

3. 同步原语

  • 信号量
  • 条件变量
  • 读写锁

7.代码示例

以下是一个全面的C示例,演示了竞争条件和其预防方法:

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

int shared_counter = 0;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

// Race Condition Version
void* increment_without_mutex(void* arg) {
    for (int i = 0; i < 100000; i++) {
        shared_counter++;  // Unsafe increment
    }
    return NULL;
}

// Safe Synchronized Version
void* increment_with_mutex(void* arg) {
    for (int i = 0; i < 100000; i++) {
        pthread_mutex_lock(&mutex);
        shared_counter++;  // Safe increment
        pthread_mutex_unlock(&mutex);
    }
    return NULL;
}

int main() {
    pthread_t threads[2];

    // Demonstrate Race Condition
    printf("Demonstrating Race Condition:\n");
    shared_counter = 0;
    for (int i = 0; i < 2; i++) {
        pthread_create(&threads[i], NULL, increment_without_mutex, NULL);
    }
    
    for (int i = 0; i < 2; i++) {
        pthread_join(threads[i], NULL);
    }
    printf("Final Counter (Unsafe): %d\n", shared_counter);

    // Demonstrate Safe Synchronization
    printf("\nDemonstrating Safe Synchronization:\n");
    shared_counter = 0;
    for (int i = 0; i < 2; i++) {
        pthread_create(&threads[i], NULL, increment_with_mutex, NULL);
    }
    
    for (int i = 0; i < 2; i++) {
        pthread_join(threads[i], NULL);
    }
    printf("Final Counter (Safe): %d\n", shared_counter);

    return 0;
}

8.参考资料和进一步阅读

  1. "Operating Systems: Three Easy Pieces" by Remzi H. Arpaci-Demetriu
  2. "Art of Multiprocessor Programming" by Maurice Herlihy
  3. POSIX Thread Programming
  4. Linux Manual Pages on Pthread

9.总结

竞态条件是并发编程中的关键挑战。通过了解它们的本质、检测方法以及预防策略,开发者可以创建更健壮、更可靠的并发系统。

关键要点:

  • 竞态条件源于对共享资源的无控制并发访问
  • 适当的同步至关重要
  • 存在多种预防策略
  • 持续学习和精心设计是必须的

第12天将深入探讨互斥量和信号量。

注意:使用-pthread标志编译代码:gcc -pthread race_conditions.c -o race_conditions