进程并发常见问题基于信号量解决方法总结:生产者/消费者问题、读/写者问题、银行家算法、哲学家进餐(待补充)

一、信号量

  • 信号量是一个与队列有关的整型变量。
  • 可以初始化成非负数;
  • semWait操作使信号量减1。若值为负数,则执行semWait的进程阻塞,否则继续执行;
  • semSignal操作使信号量加1。若值小于或等于0,则被semWait操作阻塞的进程被解除阻塞。

信号量原语semWait和semSignal的定义

strcut semaphore{
    int count;
    aueueType queue;
};

void semWait(semaphore s) {
    s.count--;
    if(s.count < 0) {
        place this process in s.queue;
        block this process;
    }
}

void semSignal(semaphore s) {
    s.count ++;
    if(s.count <= 0) {
        remove a process P from s.queue;
        place process P on ready list;
    }
}

信号量实现互斥

const int n;
semaphore s = 1;
void P(int i) {
    while(true) {
        semWait(s);
        operate;
        semSignal(s);
    }
}

void main() {
    parbegin(P(1), P(2), ...,P(n));
}

总结

信号量

  • 一个信号量可用于n个进程的同步互斥;且只能由semWait、semSignal操作修改。
  • 用于互斥时,S初值为1,取值为1~ - (n-1) (相当于临界区的通行证,实际上也是资源个数)
    S=1:临界区可用
    S=0:已有一进程进入临界区
    S<0:临界区已被占用,|S|个进程正等待进入
  • 用于同步时,S初值>=0
    S>=0:表示可用资源个数
    S<0: 表示该资源的等待队列长度

semWait、semSignal操作

  • semWait(S):请求分配一个资源。
  • semSignal(S):释放一个资源。
  • semWait、semSignal操作必须成对出现。
  • 用于互斥时,位于同一进程内;
  • 用于同步时,交错出现于两个合作进程内。
  • 多个semWait操作的次序不能颠倒,否则可能导致死锁。
  • 多个semSignal操作的次序可任意。

二、生产者/消费者问题

问题描述:
有一个或多个生产者生产某种类型的数据,并放置在缓冲区中;
有一个消费者从缓冲区中取数据,每次取一项;
系统保证避免对缓冲区的重复操作,即任何时候只有一个主体(生产者或消费者)可以访问缓冲区;
缓存已满时,生产者不能继续添加数据;
缓存已空时,消费者不能继续移走数据。

producer:

    
    while(true) {
        /* produce item v */
        while((in + 1) % n == out) //等待缓存有空位
            /* doing nothing */
        b[in] = v;
        in = (in + 1) % n;
    }   
    

consumer:


    while(true) {
        while(in == out) //此时缓存为空,等待生产者生产放入缓存后才可消费
            /* doing nothing */
        w = b[out];
        out = (out + 1) % n;
        /* consume item w */
    }

有限缓冲区:

process_concurrent_finite_buffer.png

使用信号量解决有限缓冲区生产者消费者问题:

n 表示已生产产品的数量
s 用来控制互斥
e 表示空闲空间数目


    semaphore n = 0, s = 1, e = buf - size;

    void producer() {
        while(true) {
            produce();
            semWait(e);
            semWait(s);
            append();
            semSignal(s);
            semSignal(e);
        }
    }

    void consumer() {
        while(true) {
            semWait(n);
            semWait(s);
            take();
            semSignal(s);
            semSignal(e);
            consume();
        }
    }


例题

  1. 桌子上有一个盘子,可以存放一个水果。父亲总是放苹果到盘子中,而母亲总是放香蕉到盘子中;儿子专等吃盘中的香蕉,而女儿专等吃盘中的苹果。

分析:
生产者-消费者问题的一种变形,生产者、消费者以及放入缓冲区的产品都有两类(苹果和香蕉),但每类消费者只消费其中固定的一种产品(儿子消费香蕉,女儿消费苹果)。

数据结构: semaphore dish, apple, banana;
dish: 表示盘子是否为空,用于控制互斥
apple:表示盘子中是否有苹果,初始值为0
banana:表示盘子中是否有香蕉,初始值为0

    
process father() {
    semWait(dish);
    put the apple in the dish;
    semSignal(apple);
}

process mother() {
    semWait(dish);
    put the banana in the dish;
    semSignal(banana);
}

process son() {
    semWait(banana);
    get the banana from the dish;
    semSignal(dish);
}

process daughter() {
    semWait(apple);
    get the apple from the dish;
    semSignal(dish);
}

  1. 在一个盒子里,混装了数量相等的黑白围棋子。现在用自动分拣系统把黑子、白子分开,设分拣系统有两个进程P1和P2,其中P1拣白子,P2拣黑子。规定每个进程每次拣一子,当一个进程在拣时,不允许另一个进程去拣;当一个进程拣了一子时,必须让另一个进程去拣。试用信号量协调两个进程的并发执行。

分析:
实际上就是两个进程的同步问题,也就是拣了一个白棋子才能拣一个黑棋子,两者成合作关系

数据结构:semaphore s1, s2;
s1 和s2 分别表示可拣白子和黑子,不失一般性,若令先拣白子。初值, s1=1; s2=0;


process p1() {
   while(true){
       semWait(s1);
       Pick a white chessman;
       semSignal(s2);
   }
}

process p2() {
   while(true){
       semWait(s2);
       Pick a white chessman;
       semSignal(s1);
   }
}

  1. 假设一个阅览室有100个座位,没有座位时读者在阅览室外等待;每个读者进入阅览室时都必须在阅览室门口的一个登记本上登记座位号和姓名,然后阅览,离开阅览室时要去掉登记项。每次只允许一个人登记或去掉登记。用信号量操作描述读者的行为。

分析:
实际上是一个非常简单的同步-互斥问题,登记时需要保证互斥,室内人数在100之内时,无需等待,大于100人是,开始需要等待室内有人出来后方可有人入室

数据结构:
strcut {
char name[10];
int number;
} a[100]; //表示进入阅览室的小朋友
semaphore mutex, seatcount;
mutex: 用来控制互斥,初始值为1
seatcount: 对空座位进行计数,初始值为100;

初始化入室人员信息
for(int i = 0; i < 100; i++){
    a[i].number = i;
    a[i].name = null;
}
   
process readeri(char readername[]) {
   semWait(seatcount);     //等待空余作为,若人数未满100,则直接进入,到达100,则等待
   semWait(mutex);         //控制互斥

   /* 进入是登记 */
   for(int i = 0; i < 100; i++)
       if(a[i].name == null){  //找到名字为空的座位
           a[i].name = readername;
           break;
       }
   reader get the seat nember i;
   semSiganl(mutex);
   go into the reading room and sit down at the seat number i.
   
   /* 离开时登记 */
   semWait(mutex);
   a[i].name = null;  
   semSignal(mutex);
   semSignal(seatcount);
   leave reading room;
}

二、读/写者问题
描述:
有一个由多个进程共享的数据区,一些进程只读取这个数据区中的数据,一些进程只往数据区中写数据。并须满足以下条件:
任意多的读进程可以同时读文件;
一次只有一个写进程可以写文件;
如果一个写进程正在写文件,那么禁止任何读进程读文件。

读者优先

分析:
当一个读进程开始访问数据区时,只要至少有一个读进程正在读,就为读进程保留对这个数据区的控制权,因此,写进程有可能处于饥饿状态。

数据结构:
readcount: 控制wsem的的设置
wsem: 当没有读进程正在读时,第一个试图读的读进程需要在wsem上等待; 当至少有一个读进程在读时,随后的读进程无需等待直接进入。
x: 用于确保readcount被正确更新。

    
    int readcount;
    semphore x = 1, wsem = 1;
    void reader() {
        while (true) {
        semWait(x);
        readcount++;
        if(readcount==1)
            semWait(wsem);  //如果是第一个读者,则要控制wsem
        semSignal(x);
        READUNIT();   
        semWait(x);
        readcount--;
        if(readcount==0)
        semSignal(wsem);
        semSignal(x);
        }

    }

    void writer(){
         while (true) {
            semWait(wsem);
            WRITEUNIT();
            semSignal(wsem);
        }
    }


实例:
独木桥问题:东、西向汽车过独木桥。桥上无车时允许一方汽车过桥,待全部过完后才允许另一方汽车过桥。用信号量操作写出同步算法。(提示:参考读者优先的解法)

数据结构:

mutex1/mutex2: 用于确保count1/count2被准备更新
count1/count2: 控制wait的设置
wait: 当没有车同向的车通过独木桥时,第一辆通过的车需要在wait上等待; 当至少有一辆同向的车通过时,随后同方向的车无需等待直接进入。

        semaphore wait=1, mutex1=1, mutex2=1;
        int count1=0, count2=0; 

        process P east(){
        semWait(mutex1);
        count1++;
        if(count1==1)   semWait(wait);
        semSignal(mutex1);
        through the singal-log bridge;
        semWait(mutex1);
        count1--;
        if(count1==0)   semSignal(wait);
        semSignal(mutex1);
   }

    process P west(){
        semWait(mutex2);
        count2++;
        if(count2==1)   semWait(wait);
        semSignal(mutex2);
        through the singal-log bridge;
        semWait(mutex2);
        count2--;
        if(count2==0)   semSignal(wait);
        semSignal(mutex2);
   }

待整理。。。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,772评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,458评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,610评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,640评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,657评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,590评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,962评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,631评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,870评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,611评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,704评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,386评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,969评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,944评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,179评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,742评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,440评论 2 342

推荐阅读更多精彩内容