8.Lab Sevent —— Multithreading

8.Lab Sevent —— Multithreading 发现自己之前写教程时忘记放自己代码仓库地址了若是有需要的友友可以直接看我仓库代码1664178416/xv6-lab-2020: MIT 6.S081实验代码若有帮助也请不要吝啬你手里的star这对我非常重要感谢感谢首先切换到thread分支git checkout thread make cleanUthreadswitch between threads为用户级线程系统设计上下文切换机制xv6中已经放了两个文件user/uthread.c和user/uthread_switch.S以及一个规则运行在Makefile中以构建uthread程序。uthread.c包含大多数用户级线程包以及三个简单测试线程的代码。实现用户级线程切换比起xv6中实现内核级线程这个更简单一些因为用户级线程不需要设计用户栈和内核栈用户页表和内核页表切换等等1.定义存储上下文的结构体tcontextkernel/proc.h同时修改uthread.cuser/uthread.c中的thread结构体2.类似按照kernel/swtch.S,修改user/uthread_switch.S中写入如下代码3.更改thread_scheduler(user/uthread.c)进行线程的轮询并找到下一个需要执行的切换其中thread_yield函数是默认提供好的Using threads在文件notxv6/ph.c中包含一个简单的哈希表如果单个线程使用该哈希表是正确但是多个线程使用时该哈希表不正确可以在主目录中输入如下命令make ph ./ph 1结果如下ph的参数指定在哈希表上执行put和get的线程数可以看到没有missing100000 puts 也对应 10000 gets但是当使用多个线程的时候例如2结果如下$ ./ph 2 100000 puts, 4.385 seconds, 23044 puts/second 1: 16579 keys missing 0: 16579 keys missing 200000 gets, 8.422 seconds, 23597 gets/second可以看到丢失了不少但数字确实貌似是二倍的样子pu运行两个基准程序通过put()将许多键添加到哈希表并以秒为单位打印puts的速率然后它使用get()从哈希表中获取键打印由于puts而应该在哈希表中单丢失的键的数量。然而16579 keys missing的两行代表丢失了大量的键所以这里需要使用多进程必须进行上锁设定了五个散列桶根据键除以5的余数来确定插入到哪个散列桶中插入的方式是头插法造成数据丢失原因假设现在有两个线程T1和T2两个线程都走到put函数且假设两个线程中 key%NBUCKET相同两个线程同时调用insert(key, value, table[i], table[i])第一个参数是传需要插入的内存地址第二个参数是需要插入的某块内存插入到该内存之前insert是通过头插法实现的。如果先insert的线程还未返回另一个线程就开始insert那么前面的数据会被覆盖1.在notxv6/ph.c为每个散列桶定义一个锁将五个锁放在一个数组中并进行初始化2.在put函数中对insert函数上锁get不需要因为不会修改只是读加锁后测试Barrier实现一个内存屏障(Barrier)应用程序中的某个点所有参与的线程都必须在此点上等待知道其他所有参与的线程也到达该点在notxv6/barrier.c中包含一个残缺的屏障实现$ make barrier $ ./barrier 2 barrier: notxv6/barrier.c:42: thread: Assertion i t failed.2 指明了在屏障上同步的线程数每个线程执行一个循环每次循环迭代中线程都会调用barrier()然后以随机微秒数休眠如果一个线程在另一个线程到达屏障之前离开屏障将触发断言。希望达到的效果是每个线程在barrier()中阻塞直到nthreads的所有线程都调用了barrier()看一下notxv6/barrier.c中的代码逻辑已经完成了barrier函数的实现#include stdlib.h #include unistd.h #include stdio.h #include assert.h #include pthread.h static int nthread 1; static int round 0; // 互斥锁条件变量到达屏障的线程数、轮数 struct barrier { pthread_mutex_t barrier_mutex; pthread_cond_t barrier_cond; int nthread; // Number of threads that have reached this round of the barrier int round; // Barrier round } bstate; //初始化屏障 static void barrier_init(void) { assert(pthread_mutex_init(bstate.barrier_mutex, NULL) 0); assert(pthread_cond_init(bstate.barrier_cond, NULL) 0); bstate.nthread 0; } //屏障函数已经实现 static void barrier() { // YOUR CODE HERE // // Block until all threads have called barrier() and // then increment bstate.round. // //申请锁互斥操作 pthread_mutex_lock(bstate.barrier_mutex); // judge whether all threads reach the barrier if(bstate.nthread ! nthread) { // not all threads reach pthread_cond_wait(bstate.barrier_cond,bstate.barrier_mutex); // wait other threads } else { // all threads reach bstate.nthread 0; // reset nthread bstate.round; // increase round pthread_cond_broadcast(bstate.barrier_cond); // wake up all sleeping threads } pthread_mutex_unlock(bstate.barrier_mutex); } //每个线程执行的函数 static void * thread(void *xa) { long n (long) xa; long delay; int i; for (i 0; i 20000; i) { int t bstate.round; //检查是否实现了所有线程共同达到屏障的效果 assert (i t); //等待所有线程到达屏障 barrier(); usleep(random() % 100); } return 0; } int main(int argc, char *argv[]) { pthread_t *tha; void *value; long i; double t1, t0; if (argc 2) { fprintf(stderr, %s: %s nthread\n, argv[0], argv[0]); exit(-1); } //参数指定线程数量 nthread atoi(argv[1]); tha malloc(sizeof(pthread_t) * nthread); // srandom 是 C 标准库中的一个函数用于设置伪随机数生成器PRNG的起始种子 -- 输出只是伪随机而不是真正的随机数 srandom(0); barrier_init(); //创建n个线程执行 for(i 0; i nthread; i) { assert(pthread_create(tha[i], NULL, thread, (void *) i) 0); } for(i 0; i nthread; i) { assert(pthread_join(tha[i], value) 0); } printf(OK; passed\n); }