数据竞争

数据竞争


(所以不上锁不就没有死锁了吗?)

2024 南京大学《操作系统:设计与实现》
数据竞争

数据竞争

不同的线程同时访问同一内存,且至少有一个是写

  • 两个内存访问在 “赛跑”,“跑赢” 的操作先执行
  • 例子:共享内存上实现的 Peterson 算法

center

2024 南京大学《操作系统:设计与实现》
数据竞争

数据竞争 (cont'd)

“跑赢” 并没有想象中那么简单

center

2024 南京大学《操作系统:设计与实现》
数据竞争

数据竞争:例子

以下代码概括了你们遇到数据竞争的大部分情况

  • 不要笑,你们的 bug 几乎都是这两种情况的变种

Case 1: 上错了锁

void T_1() { spin_lock(&A); sum++; spin_unlock(&A); }
void T_2() { spin_lock(&B); sum++; spin_unlock(&B); }

Case 2: 忘记上锁

void T_1() { spin_lock(&A); sum++; spin_unlock(&A); }
void T_2() { sum++; }
2024 南京大学《操作系统:设计与实现》
数据竞争

我的翻车现场

不同的线程同时访问同一内存,且至少有一个是写

void wait_next_beat(int expect) {
    // This is a spin-wait loop.
retry:
    mutex_lock(&lk);
    // This read is protected by a mutex.
    int got = n;
    mutex_unlock(&lk);

    // Case 2: 忘记上锁
    if (n != expect) goto retry;
}
  • 实际不是忘记上锁,是用错了变量
  • 更致命的:bug (error state) 很难触发 “Heisenbugs”
2024 南京大学《操作系统:设计与实现》
数据竞争

为什么不要笑?

实际系统面临更复杂的情况

  • “内存” 可以是地址空间中的任何内存
    • 可以是全部变量
    • 可以是堆区分配的变量
    • 可以是栈
  • “访问” 可以是任何代码
    • 可能发生在你的代码里
    • 可以发生在框架代码里
    • 可能是一行你没有读到过的汇编代码
    • 可能是一条 ret 指令
2024 南京大学《操作系统:设计与实现》
数据竞争

数据竞争:请大家记得

用锁保护好共享数据


消灭一切数据竞争

2024 南京大学《操作系统:设计与实现》