Lab6 Multithreading


Lab链接:Lab: Multithreading

Lab源码:momo/MIT-6S081/thread - Gitee.com

1 Uthread: switching between threads​​

题目

xv6需要进入内核态完成进程切换,本lab需要你在用户空间完成线程切换。

思路

参考 xv6 中进程上下文切换的实现方式,用户态线程的切换也遵循类似机制:

  1. 定义线程上下文结构体context,并在线程结构体thread中添加context结构体字段以存储线程运行上下文
  2. 在线程调度thread_scheduler() 函数中保存当前线程的上下文,加载待运行线程的上下文

线程上下文的内容?为什么只保存 callee-saved 寄存器?

与 xv6 进程上下文 context 结构体类似,线程上下文也包含程序计数器ra、栈指针sp 及 callee-saved 寄存器

由于在调用 swtch() 函数之前,编译器会自动将 caller-saved 寄存器压入当前栈帧,因此 swtch() 作为被调用函数,仅保存 callee-saved 寄存器即可。相比之下,用于陷入内核的 必须完整保存所有寄存器状态,由于中断或异常可能在用户程序执行的任何时候发生,调用trampoline切换至内核态前并未主动保存任何寄存器,因此trapframe需要保留全部寄存器以确保执行现场可被正确恢复

线程栈数据存储在哪?

一个关键问题在于如何保存线程相关的栈数据,尤其是确定每个线程的栈帧位置,一开始我百思不得其解

直到我看到thread结构体中的stack字段,猜想可能是通过在线程结构体 thread 中内嵌一个专属于该线程的字符数组 stack作为栈数据存储的地方,函数调用、局部变量等操作都在该空间内进行

进程不一定专门有个区域作为栈,可以在任意区域为线程分配一个私有地址空间作为栈

由于 xv6 中栈的生长方向为低地址,线程栈指针初始化值应为 stack+STACK_SIZE-1,从而确保栈操作在预分配范围内正确进行

需要特别说明的是,线程栈的存储方式具有很大的灵活性。在实现上,并不一定需要为线程预留专门的栈区域,而是可以在任意合适的地址空间为每个线程动态分配一块私有内存作为其运行时栈

源码

线程上下文结构体context和线程切换程序swtch()与xv6进程切换类似,不再赘述。

线程初始化时程序计数器pc(ra)和栈指针sp的初始值是关键点:

void thread_create(void (*func)()) {
  t->context.ra = (uint64)func;
  t->context.sp = (uint64)(((char*)t->stack) + STACK_SIZE - 1);
}

2 Using threads​​

题目

在多核机器,使用pthread 多线程库实现使用多线程向哈希表写入大量键值对

运行多线程程序,观察与单线程程序相比,是否出现键丢失和速度提升的现象

思路

出现键值对丢失现象的根本原因在于:未对共享的哈希表数据实施线程同步保护。当多个线程并发写入时,不同键值可能被映射至同一哈希桶。若多个线程同时向该桶中执行写入操作,后一次写入会覆盖前一次的结果,从而导致键值对丢失。

本着“先实现,后优化”的原则,首先确保多线程环境下不发生键值对丢失,再通过缩小锁的粒度来提升程序运行效率。

由于本实验所有写操作集中在前阶段,读操作集中在后阶段,读写操作在时间上完全隔离的。因此仅需对put()写入函数实施线程同步保护,而get()读取函数无需加锁即可安全访问。

优化锁粒度的关键在于区分共享资源的实际竞争情况。当前实现中所有哈希桶共享同一个全局锁,这会导致不必要的同步开销。而不同哈希桶之间不存在写入冲突(即键值对不会跨桶覆盖),因此可以将锁的粒度从全局锁优化为以单个哈希桶为单位的细粒度锁。

3 Barrier​​

题目

实现一个屏障(Barrier)同步机制,要求多个线程在执行过程中先后进入屏障点,先到达的线程需要等待,直到所有线程都到达该屏障后,才能一同进入下一轮继续执行。

思路

屏障同步是协调多个线程并行执行的一种常见方式,其核心在于使用条件变量和互斥锁实现线程间的等待与唤醒。

具体实现时,每个线程进入屏障时首先获取互斥锁,保护共享状态不被并发修改,线程到达数量递增。若未达到总线程数,则该线程在条件变量上等待;若已达到总线程数,则重置计数器,增加轮次,并通过广播唤醒所有等待的线程。最后,释放互斥锁,确保所有线程同步进入下一阶段。

源码

static void barrier() {
  pthread_mutex_lock(&bstate.barrier_mutex);
  bstate.nthread += 1;
  if(bstate.nthread < nthread) {
    pthread_cond_wait(&bstate.barrier_cond, &bstate.barrier_mutex);
  } else {
    bstate.nthread = 0;
    bstate.round += 1;
    pthread_cond_broadcast(&bstate.barrier_cond);
  }
  pthread_mutex_unlock(&bstate.barrier_mutex);
}

文章作者: AthenaCrafter
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 AthenaCrafter !
  目录