gem-universe / blog

0 stars 0 forks source link

[操作系统]5. 多处理器编程 #5

Open supergem3000 opened 9 months ago

supergem3000 commented 9 months ago

5. 多处理器编程:从入门到放弃 (jyywiki.cn)

并发

操作系统作为“状态机的管理者”,引入了共享的状态。(如文件等等) 操作系统是最早的并发程序。


def Tprint(name):
sys_write(f'{name}')

def main(): for name in 'AB': sys_spawn(Tprint, name)

并发程序执行结果可能不确定。比如这个程序最终打印可能是AB也可能是BA。

思考:每一个进程,有自己的内存空间,也就是有一个栈、堆,如下。

stack
---------
heap
---------
code

但是进程内的每一个线程,也有自己的栈。这时候栈在哪呢?
# 入门到放弃1:原子性
山寨支付宝示例:
```c
#include "thread.h"

unsigned long balance = 100;

void Alipay_withdraw(int amt) {
  if (balance >= amt) {
    usleep(1);  // Unexpected delays 程序中途被不知道什么打断
    balance -= amt;
  }
}

void Talipay(int id) {
  Alipay_withdraw(100);
}

int main() {
  create(Talipay);
  create(Talipay);
  join(); // 等前面创建的线程都完成
  printf("balance = %lu\n", balance);
}

两个线程都检查余额足够,结果都扣了钱。导致余额变负数(无符号数变特别大)

#include "thread.h"

#define N 100000000

long sum = 0;

void Tsum() {
  for (int i = 0; i < N; i++) {
    sum++;
  }
}

int main() {
  create(Tsum);
  create(Tsum);
  join();
  printf("sum = %ld\n", sum);
}

甚至这个求和程序,都不能正确得到2N。把sum++改成一个incq汇编指令,还是不对。

“处理器一次执行一条指令”的基本假设在今天的计算机系统上不再成立。(多处理器)

入门到放弃2:执行顺序

还是上面求和的例子,如果使用O1优化,发现输出结果是100000000,如果是O2优化,发现输出结果是20000000。

保证执行顺序:

ARM/x86等等架构,处理器内存模型也都不一样。 说了这么多放弃的内容,其实就是想说:写多线程代码时,不要试图去完全搞清楚处理器的各种细节,老老实实用锁。