8.Lab Sevent —— Multithreading

首先切换到thread分支

git checkout thread
make clean

Uthread:switch between threads

为用户级线程系统设计上下文切换机制

xv6中已经放了两个文件:

  • user/uthread.cuser/uthread_switch.S

  • 以及一个规则:运行在Makefile中以构建uthread程序。

  • uthread.c包含大多数用户级线程包,以及三个简单测试线程的代码。

实现用户级线程切换,比起xv6中实现内核级线程,这个更简单一些,因为用户级线程不需要设计用户栈和内核栈,用户页表和内核页表切换等等

1.定义存储上下文的结构体tcontext(kernel/proc.h)同时修改uthread.c(user/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的线程数,可以看到没有missing,100000 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");
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值