线程切换


Uthread: switching between
threads
针对用户级的线程系统设计并实现一个上下文切换
理解了lecture
中的线程切换就比较简单,基本上模仿内核模式写就行了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| struct thread{ char stack[STACK_SIZE]; int state; struct context context; };
void thread_create(void (*func)()) { ... t->context.ra = (uint64)func; t->context.sp = (uint64)(t->stack + PGSIZE); }
thread_switch((uint64)&t->context, (uint64)¤t_thread->context);
|
疑难点:设置线程运行函数那一个地方还是需要理解一下(当然lecture
中以及xv6
中也有相应的部分可以参考),就是为什么设置了两个寄存器就可以了?因为每次切换上下文之后返回的都是当前ra
寄存器指向的位置,所以想要使函数可以运行在对应线程上,只要设置该线程context
里的ra
寄存器。sp
寄存器是为函数的运行分配栈空间。
Using threads
解决多线程读写哈希表(数组+链表)引发的race-condition
现象产生的原因:阅读ph.c
中代码可以发现,针对一个新的key,是会插入这个key对应桶中链表的头部。但是一旦有两个进程并发执行,而不加措施,就会有entry
丢失。
1 2 3 4 5 6 7 8 9
| static void insert(int key, int value, struct entry **p, struct entry *n) { struct entry *e = malloc(sizeof(struct entry)); e->key = key; e->value = value; e->next = n; *p = e; }
|
解决方案:加锁
1 2 3 4 5 6 7 8 9 10
| pthread_mutex_t lock;
pthread_mutex_init(&lock, NULL);
pthread_mutex_lock(&lock); insert(...); pthread_mutex_unlock(&lock);
|
加锁的粒度:经过思考可以发现,很多情况下是不需要加锁的,真正产生冲突的原因是insert函数,所以最小的锁粒度应该针对每一次的insert
函数。
Barrier
实现一个同步点:所有的线程要等待其他线程到达该点
需要学习一下xv6
中的sleep
和wakeup
函数(lecture
13)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| static void barrier() { pthread_mutex_lock(&bstate.barrier_mutex); bstate.nthread++; if (bstate.nthread != nthread) { pthread_cond_wait(&bstate.barrier_cond, &bstate.barrier_mutex); } else { bstate.nthread = 0; bstate.round ++; pthread_cond_broadcast(&bstate.barrier_cond); } pthread_mutex_unlock(&bstate.barrier_mutex); }
|