Reader函数 1. Mutex上锁 2. Rc加1。如果rc等于1,w上锁 3. Mutex解锁 4. 读操作 5. Mutex上锁 6. Rc减1。如果rc等于0,w解锁 Mutex解锁 Writer函数 1. W上锁 2. 写操作 3. W解锁 测试函数: 新建一个写者和两个读者,并且让他们分别写(读)10次。
测试结果截图:
Mutex保护共享变量rc,因为可以允许多个读者处于读状态,因此rc可能会被多个读者同时访问,需要保护起来。 W互斥读者和写者,第一类读写者问题是读者优先,因此若临界区内有读者,写者就会被w阻塞在临界区外,直到所有读者离开临界区。 写者能够进入临界区当且仅当临界区内没有读者。但写者在临界区内的时候,读者不能进入。 读者可以同时读。(start和finish之间可以认为是临界区)
写者在临界区里时,即使发生了interrupt(线程切换),也无法让读者进入。即读者和写者不能同时访问临界区
11
三:遇到的困难以及解决方法
困难1:我在完成lab2线程调度实验中,在readyToRun函数中对当前线程执行了yield操作,导致lab3中V函数的原子性被破坏。(V函数会调用readyToRun函数)
解决方式:将线程Yield的过程单独封装成一个函数,readyToRun函数仅仅负责将当前线程放入可执行队列当中。对于新建的一个线程(可能可以抢占当前线程),先调用该函数,判断是否抢占。如果抢占,则让当前线程yield;否则,调用readyToRun函数。
困难2:思考如何实现condition的wait函数,曾一度觉得synchlist中会造成死锁
解决方式:详细了解了管程机制之后,发现只要在wait函数中,先对传入的Lock参数执行释放(Release)操作,当前线程再进入休眠状态,问题就可以解决了。
困难3:在写“生产者-消费者”问题的测试函数时,从队列中读出来的值总是等于最后一个加入队列的值(比如加入队列1,2,3,4,读出来会是4,4,4,4)
解决方式:由于传入的是(void *),是一个指针。因此我直接传入了for循环中的i的地址,这就导致了i的值虽然在变化,但i的地址是不变的,所以读出来总会等于最后一个加入队列的值。之后专门创建了一个buffer数组,使得不同的值对应不同的地址,问题就解决了。
困难4:一开始是用lock的acquire和release实现condition,但测试函数结果和预期的不相符
解决方式:经过研究,我发现,造成错误的原因,是lock的release函数,会使得value值增加。这就意味着,即使等待信号的队列为空,signal函数也会释放一个资源。这样之后,若还有wait函数调用,就不会被阻塞,而是直接通过。这不符合管程中wait和signal的语义(我查了上学期操作系统的课件,课件上说,若是等待队列为空,则signal函数为空操作)。于是我将Lock换成了队列,当队列不为空的时候才让signal和broadcast函数起作用。
四:收获及感想
这次的lab消耗了比预期多的时间,主要问题在于condition的实现。需要仔细了解了管程机制之后,才知道如何编写。详情可参看【困难4】。此外,测试函数也需要开动脑筋,
12
不仅需要写出来,而且还要通过合适位置的输出,来判断程序是否按照自己预期的样子运行。这个过程同样需要对同步机制有较好理解,才能理清楚。
做了这次lab,觉得自己对于同步机制又多了一层了解,而且代码和报告均是自己独立完成,没有参考网上前人的报告,觉得挺高兴的!
内容五:参考文献
[1]陈向群 同步机制 操作系统课件2014版
[2]linux同步机制 网络博客
[3]小组成员:王丰 condition的实现方式 讨论
13