Chinaunix首页 | 论坛 | 博客
  • 博客访问: 140954
  • 博文数量: 54
  • 博客积分: 2682
  • 博客等级: 少校
  • 技术积分: 580
  • 用 户 组: 普通用户
  • 注册时间: 2009-10-24 20:56
文章分类
文章存档

2012年(2)

2011年(10)

2010年(28)

2009年(14)

我的朋友

分类: 系统运维

2012-01-16 09:51:28

这是我今天偶然想到的一个把monitor程序翻译成semaphore程序的标准步骤,经过三步,monitor程序就可以翻译成semaphore程序,功能一样,虽然可能形式上不是最简的,但是对于有些直接通过semaphore实现很难思考,用semaphore实现很容易产生死锁的问题,我觉得可以尝试先用monitor写,因为monitor简单直观,程序好些,把逻辑搞清楚基本上不会死锁,然后再按我说的三步骤翻译成semahpore程序。我试过了好几个例子,我觉得都是对的。当然这里面不排除我的方法是错的,也可能我的方法是对的,但早就在monitor创始人的论文里解释过了,但是不管咋样,我觉得似乎这个办法非常有意思,而且可行。若需要讨论或需要Java程序测试代码的请与我联系。 (这些代码在我机子上用java程序调试通过,monitor程序是用java的Lock,ReentrantLock, Lock.newCondition模拟实现,Semaphore是用自己实现的类,当然java自身也有Semaphore类,能否用未知,但我相信能用)

(补充,经过我再次尝试一些转换,虽然我仍然相信这个转换是正确的,但是不得不承认转换出来的代码和已有的semaphore代码结构上有区别,所以这个方法仍然不能代替对原有semaphore程序的理解,我个人认为之所以出现这种原因,可能有两点,第一,用semaphore来思考本身就有很多种思路;第二,由于是monitor转换过来的,所以难免有些地方是redundant了,因为我听说过一个说法,虽然semaphore程序难写,难以维护,难以scale,但是一旦写出来了,没废话,效率还是不错)

 

若没接触过monitorsemaphore程序的可以先阅读wiki上的东西:

另外,如果你想有效的调试、可视化你的多线程Semaphore程序,那么强烈建议使用一个计算机教授Allen B. Downey写的软件swampy中的sync模块,他还有一个讲解这个软件的视频在youtube上。

这是我用他的软件调试我自己写的一个reader-writer problem with reader priority的画面,很有趣,很清晰:

(图中现在的画面展示的是两个Writer被卡住在外层信号量,有一个Writer卡在内层信号量,而Reader都跑得很欢快,清晰展示了读者优先)


接下来详细介绍我想的这三个步骤:

1. 第一步,把monitor程序中所有的"begin:"后面加一个P(mutex);(注意,这里暂时不需要把所有的end前面加个V(mutex),这个在第三步讲;注意,beginend是一个monitor procedure伪代码的开始和结束);

2. 第二步,所有monitor程序中的shared variable原封不动的加入到semaphore程序,他们的代码也不需要改变;

3. 第三步,对于每一个monitor程序中出现的conditional variable,比如名叫condconditional variable,都加一个对应的semaphore,比如cond_sem;然后再加一个名为cond_waitshared variablesemaphore程序中(cond_wait名字来源于它的意义,它代表有多少人正在等cond_sem);然后把如下三个东西按一下规则替换:

i)monitor程序中用到了cond.queue的,全部翻译成cond_wait。比如if(cond.queue)翻译成if(cond_wait>0);再比如if(!cond.queue)翻译成if(cond_wait==0)

ii)monitor程序中用到了cond.wait()的,全部翻译成{cond_wait++;V(mutex);P(cond_sem);cond_wait--;}

iii)monitor程序中用到了cond.signal()的,全部翻译成if(cond_wait>0) V(cond_signal) else V(mutex)

(注意,第三小步这里需要灵活应变,因为有可能cond.signal本身就是放在if里的,比如if(readers==0) cond1.signal(),那么要翻译成if(readers==0) { if(cond1_wait>0) V(cond1_signal) else V(mutex) } else { V(mutex);} 其实可以简写为 if(readers==0&&cond1_wait>0) { V(cond1_signal); } else {V(mutex);}

 

第三步是其中的关键之处,在第三步的ii)中,{cond_wait++; V(mutex); P(cond_sem); cond_wait--;}之所以这么做,是因为首先我们要释放我们获取的mutex,这一点非常类似于monitor程序中的cond.wait()先释放monitor,然后再等。但是monitor程序中,cond.wait()里一旦被唤醒是自动获取monitor的,但是翻译的代码里为什么不是{cond_wait++; V(mutex); P(cond_sem); P(mutex) ;cond_wait--;}?这就是我个人觉得比较精巧的地方,这个地方也确实能解决实际问题,等下会解释一个例子(其他例子比如producer&consumer我也试过,也是可以的),这里之所以不加入P(mutex),是因为凡属唤醒它的人自己已经做过P(mutex)了,所以V(cond_sem)后,这个程序还是有mutex在手,继续下去,不会有问题。这样做有两个理由,第一,在monitor程序中,一旦有进程调用cond.signal(),我们必须保证它真的找到了一个进程在等cond.wait(),并且保证它是释放monitor后第一个执行的,我们通过不放弃mutex做到了这一点;第二,如果我真的加了P(mutex),那么程序会出现问题,因为这个时候其实出现了断层,本应该在一起的代码分成了两片,中间有可能插入别的改变状态的代码。

 

接下来看一个例子,我这只举一个例子,这个例子很难,我直接用semaphore思考想了很久没想出来,因为我觉得非常精巧,实在是难以想全,很容易出死锁。但是我可以很方便的写出它的monitor程序,这个例子就是:

 

Write a monitor-based solution to the reader–writers problem that works as

follows: If readers and writers are both waiting, then it alternates between readers

and writers. Otherwise, it processes them normally, i.e., readers concurrently and

writers serially.

 

就是读者写者问题,当有读者和写者同时等待时,我们需要交替唤醒他们。其实我可以把这个称之为第四读者写者问题,第负一个读者写者问题(请允许我暂时这样表达),是读读,写写,读写完全互斥,这个最简单,但是不能并行读;第0个读者写者问题,允许并行读;第1个读者写者问题,是读者优先;第2个读者写者问题,是写者优先;第三个读者写者问题,是按照来到的顺序进行调度;第四个读者写者问题,也就是我们马上要讨论的问题,是交替唤醒读写进程。( 这个链接给出了第一,第二,第三读者写者问题的描述和解答,可以看看)

 

首先给出monitor程序的解答,我个人觉得写monitor程序有个技巧,就是要按照 这个图去思考,先画图,再写程序,感觉顺利多了。

 

Monitor program for the 4-th reader-writer problem

Condition Variable: r_cond; w_cond

Shared Variable: writing=false; readers=0;

 

Procedure before_read:

begin:

          if(r_cond.queue)

                   r_cond.wait();

          else if (writing == true)

                   r_cond.wait();

          else if (w_cond.queue)

                   r_cond.wait();

          readers++;

          if(!w_cond.queue)

                   r_cond.signal()

end

 

Procedure after_read:

begin:

          readers--;

          if(readers==0){

                   if(w_cond.queue)

                             w_cond.signal()

          }

end

 

Procedure before_write:

begin:

          if(w_cond.queue)

                   w_cond.wait()

          else if(readers>0)

                   w_cond.wait()

          else if (writing == true)

                   w_cond.wait()

          writing = true

end

 

Procedure after_write:

begin:

          writing=false

          if(r_cond.queue)

                   r_cond.signal()

          else if (w_cond.queue)

                   w_cond.signal()

end

 

按照三个步骤翻译成semaphore程序就是:


Semaphore program for the 4-th reader-writer problem

Int: readers = 0, r_cond_wait = 0, w_cond_wait = 0;

Boolean: writing = false;

Semaphores:

mutex=1 //Guard the critical section

r_cond_sem = 0 //reader queue

w_cond_sem = 0 //writer queue


Reader:

P (mutex)

if(r_cond_wait>0)

          {r_cond_wait++;V(mutex);P(r_cond_sem);r_cond_wait--;}

eles if (writing == true)

          {r_cond_wait++;V(mutex);P(r_cond_sem);r_cond_wait--;}

else if (w_cond_wait>0)

          {r_cond_wait++;V(mutex);P(r_cond_sem);r_cond_wait--;}

readers++;

if(r_cond_wait>0 && w_cond_wait==0)

{ V(r_cond_sem);}

else

{V(mutex);}

P(mutex);

readers--;

if(readers==0){

          if(w_cond_wait>0)

                   V(w_cond_sem);

          }

          else { V(mutex);}

 

Writer:

P(mutex);

if(w_cond_wait>0)

                {w_cond_wait++;V(mutex);P(w_cond_sem);w_cond_wait--;}

else if (readers>0)

          {w_cond_wait++;V(mutex);P(w_cond_sem);w_cond_wait--;}

else if (writing==true)

          {w_cond_wait++;V(mutex);P(w_cond_sem);w_cond_wait--;}

writing = true;

V(mutex);

P(mutex);

writing =false;

if(r_cond_wait > 0)

          V(r_cond_sem)

else if (w_cond_wait>0)

          V(w_cond_sem)

else

          V(mutex)

 以上便是我今天偶然想到的一个方法,我觉得比较兴奋,当然我并不觉得这个有多神奇,因为我真的记得在monitor创始人的paper中就提到过monitor是可用Semaphore来实现的。当然这个由我自己想出来,觉得还是非常有趣的。


1/19/2012 更正:

仔细阅读了一下monitor的文章(~dwilkins/Seminar/S05/Monitors.pdf)后,觉得我的方法不是完全正确的,换句话说,我的方法只是这个论文的第三页的方法的一个特例。还是文章作者想得全面,想得深刻。

我的以上方法有个假设,就是每一个condition variable的signal操作都在monitor procedure的最后,这个signal操作之后就没有别的操作了,眼看就要离开procedure了。我们可以从论文第三页右边column的第四点明确看到这一点,当这个特殊情况成立时,我们不需要urgent和urgentcount了。

论文第三页左边column其实本质上就是怎么用semahore来实现monitor,其实按照它的思路来,任何一个monitor程序都可以换成semaphore来实现,也就是我整个文章要说的事情了。论文的方法更加全面,正确。加入了一个urgent semaphore,这个确实更加正确,特别对于signal操作之后还有statement的情况。在此佩服作者全面的见解,也惭愧自己没看全面,便不再这里瞎说一通,有兴趣的还是仔细读论文吧,看一周都不为过。

阅读(1713) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~