当前位置: 代码迷 >> 综合 >> 2022-2-7
  详细解决方案

2022-2-7

热度:115   发布时间:2023-10-14 11:32:15.0

2022-2-7

  • 并发
    • 线程的创建
      • 共享数据
  • 插叙:线程API
    • 创建
    • 完成
    • 自旋锁
      • 评价自旋锁

并发

本章将介绍为单个运行进程提供的新抽象:线程
线程与进程的不同之处在于,在进行上下文切换的时候,进程需要改变地址空间,也就是需要换新的页表,而线程不需要,不同线程共享同一个地址空间。对于并发,也即多线程而言,与单线程不同的地方在于,单线程的地址空间中只有一个栈,而多线程有多个栈
2022-2-7

线程的创建

2022-2-7
可以看到,我们先创建了两个线程,然后使用两个pthread_join来等待线程的结束,但我们无法控制这两个线程哪个先执行,线程被创建后,其执行就进入了操作系统,由系统中断来决定执行哪个,可能是打印“A”的线程先被执行,也可能是打印“B”的线程先被执行

共享数据

由于线程共享地址空间的特性,他们可以对同一堆内的变量进行操作,这就会引发一些问题
2022-2-7
2022-2-7
例如上述程序,我们期望运行完毕后,counter的值为2e7,但实际结果可能是19345221或19221041等等,这是因为不可控的调度,以及共享的变量未被上锁
给counter+1的操作可能是这样的
2022-2-7
设想线程1先被执行,这时counter的值为50,他被移入eax寄存器,且向寄存器+1,则这时eax的值为51,然后突然发生了中断,需要转入线程2执行,这时操作系统将线程1的状态(包括程序计数器,寄存器,例如eax)保存到TCB里,然后执行线程2,线程2也将counter的值(还是50)移入他的eax寄存器,并为其+1,然后返回结果,这样会导致虽然两个线程都运行了一次,但结果是51而不是我们期待的52

插叙:线程API

创建

2022-2-7

完成

2022-2-7
2022-2-7
2022-2-7
2022-2-7
注意这里Pthread_join里使用的是void**

自旋锁

自旋锁只需要一个test and set就可以实现了
2022-2-7
即返回了旧值又设置了新值,不需要担心中断
2022-2-7

评价自旋锁

锁最重要的一点是正确性:保证一次只有一个线程进入临界区。这一点自旋锁可以保证
但对于公平性:保证一个线程有进入临界区的机会。这一点无法确保,因为可能有一个倒霉的线程永远在自旋,无法访问临界区
还有一点是效率,这一点也较差,因为每个线程在等待解锁的时候,都有一段自旋时间,这段时间除了等待什么也没做,浪费了cpu