report of Threads and Locking
实验要求
实验了解
初步了解
在我们使用gcc -g -O2 ph.c -pthread成功编译后,按照实验指导书我们体会一下单线程和双线程的区别
harry@ubuntu:~/Desktop/os/Threads and Locking$ ./a.out 1
0: put time = 0.004051
0: get time = 6.730206
0: 0 keys missing
completion time = 6.734654
harry@ubuntu:~/Desktop/os/Threads and Locking$ ./a.out 2
1: put time = 0.009607
0: put time = 0.010374
1: get time = 6.664562
1: 14969 keys missing
0: get time = 6.673753
0: 14969 keys missing
completion time = 6.684481
每个线程分两个阶段(phases)运行。在第一个阶段,每个线程将NKEYS/nthread keys放入the hash table。
在第二阶段,每个线程从散列表中获取NKEYS。print语句告诉你每个阶段为每个线程花费了多长时间。
倒数第二行会告诉你有多少个key丢失了,底部的completion time告诉你应用程序的总运行时间。
原理:
#define NBUCKET 5
#define NKEYS 100000
ph.c程序主要工作是使用随机数生成10000个keys,然后利用线性hash将其插入5个哈希槽中,最后再取出每个key。
问题探究
两个发现:
(1)通过以上单个线程和双线程我们可以分析得到,双线程完成的工作是单线程的二倍,但是时间上很接近,
(2)就是在单线程运行时key的丢失为0,而双线程时对于key的丢失时很严重的.
两个问题
(1)为什么双线程会丢失大量的key?而单线程时不会
在用户没有设定线程间的调度策略时,系统默认采取基于时间片轮转的调度策略。所以当NKEYS=100000时,会出现两个线程的多次轮转,也才有可能导致key的丢失。
具体过程是当thread1刚进入insert()还未进行插入时,轮转到另一个线程2进行了插入。回到线程1,此时的n还是为空,再进行连接时,就会丢失掉线程2插入的表项。
说白了就是当第一个thread还未插入key时,另一个thread的key覆盖了原来key,所以导致了第一个线程key的丢失。
当我们把NKEYS改成10的时候,就算不用put里也不加锁,也并没有丢失任何key,说明了这一点。
(2)为什么同样的代码执行的线程会不一样?下面是我某两次实验的结果
harry@ubuntu:~/Desktop/os/Threads and Locking$ ./a.out 2
0: put time = 0.007914
1: put time = 0.008186
0: get time = 6.571305
0: 1672 keys missing
1: get time = 6.612520
1: 1477 keys missing
completion time = 6.622177
harry@ubuntu:~/Desktop/os/Threads and Locking$ ./a.out 2
1: put time = 0.016394
0: put time = 0.020926
1: get time = 7.242295
1: 2121keys missing
0: get time = 7.336555
0: 2023 keys missing
completion time = 7.358044
答:一开始,应该是在主线程里,然后主线程因为pthread_join而要等线程0和线程1结束。然后线程0与线程1都开始put,因为__sync_fetch_and_add需要等所有线程完成put操作,所以先完成put操作的子线程会print出put的时间然后阻塞等另一个线程完成put操作。然后再通过get来遍历哈希表。所以顺序不一样应该是正常的,看哪边先put完,跟机器应该也有关系。
所以线程的顺序不一样没有关系
感谢这位大佬对这个问题的解释
实验过程
通过以上我们了解到为了避免双线程丢失大量key的发生,在put和get中插入lock和unlock语句,使得丢失的键数总是0。
修改ph.c的代码内容如下:
//首先在开始处定义一个五个互斥锁
pthread_mutex_t bucket_locks[NBUCKET]; //五个是因为有五个存放key的桶,每一个都需要在使用时上锁
//然后再在put函数中加锁和解锁:
static
void put(int key, int value)
{
int i = key % NBUCKET;
pthread_mutex_lock(&bucket_locks[i]);
//上锁
insert(key, value, &table[i], table[i]);
pthread_mutex_unlock(&bucket_locks[i]);
//解锁
}
然后我们检查一下就可以发现结果正确
harry@ubuntu:~/Desktop/os/Threads and Locking$ ./a.out 2
1: put time = 0.008080
0: put time = 0.008928
0: get time = 6.399594
0: 0 keys missing
1: get time = 6.400445
1: 0 keys missing
completion time = 6.410268
我们的实验到这也就完成了
报告详细介绍了MIT6.828操作系统实验中关于线程和锁定的部分。实验对比了单线程和双线程在执行散列操作时的效率和key丢失情况。双线程虽然能完成两倍工作,但可能导致key丢失,原因是时间片轮转调度可能导致线程间的key覆盖。通过在put和get操作中添加锁,可以避免key丢失问题。
3592

被折叠的 条评论
为什么被折叠?



