三个多线程同步问题及其实现

1.生产者——消费者问题

问题描述:
生产者-消费者问题是一个经典的进程同步问题,该问题最早由Dijkstra提出,用以演示他提出的信号量机制。在同一个进程地址空间内执行的两个线程。生产者线程生产物品,然后将物品放置在一个空缓冲区中供消费者线程消费。消费者线程从缓冲区中获得物品,然后释放缓冲区。当生产者线程生产物品时,如果没有空缓冲区可用,那么生产者线程必须等待消费者线程释放出一个空缓冲区。当消费者线程消费物品时,如果没有满的缓冲区,那么消费者线程将被阻塞,直到新的物品被生产出来。
这里生产者和消费者是既同步又互斥的关系,首先只有生产者生产了,消费着才能消费,这里是同步的关系。但他们对于临界区的访问又是互斥的关系。因此需要两个信号量empty和full用于同步缓冲区,而互斥量mutex用于保证在访问缓冲区时是互斥的。
P操作首先减少信号量,表示有一个进程将占用或等待资源,然后检测S是否小于0,如果小于0则阻塞,否则占有资源进行执行。
V操作是和P操作相反的操作,首先增加信号量,表示占用或等待资源的进程减少了1个。然后检测S是否大于0,如果大于0则唤醒等待使用S资源的其它进程。

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <semaphore.h>
#include <windows.h>

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
sem_t emptys;
sem_t resource;
int cur_nums=0;

void* Producer(void*)
{
begin:
    sem_wait(&emptys);
    pthread_mutex_lock(&mutex);
    puts("producing...");
    Sleep(rand()%20);
    ++cur_nums;
    printf("there are %d available resources now.\n", cur_nums);
    pthread_mutex_unlock(&mutex);
    sem_post(&resource); //将resource加1
    Sleep(rand()%1000);
    goto begin;
}
void* Customer(void*)
{
begin:
    sem_wait(&resource); //将resource减一,如果小于0则等待
    pthread_mutex_lock(&mutex);
    puts("customing......");
    Sleep(rand()%20);
    --cur_nums;
    printf("there are %d available resources now.\n", cur_nums);
    pthread_mutex_unlock(&mutex);
    sem_post(&emptys);
    Sleep(rand()%1000+100);
    goto begin;
}

int main()
{
    sem_init(&emptys,0,15); // 表示剩余空位数, 初值为15
    sem_init(&resource,0,0); // 表示已有资源数, 初值为0
    pthread_t t1,t2;
    pthread_create(&t1,NULL,Producer,NULL);
    pthread_create(&t2,NULL,Customer,NULL);
    pthread_detach(t1);
    pthread_join(t2,NULL);
    return 0;
}

由于Customer进入Sleep的时间比Producer长,资源数量会趋向于变大。

2.读者——写者问题

问题描述:
一个数据文件或记录,统称数据对象,可被多个进程共享,其中有些进程只要求读称为“读者”,而另一些进程要求写或修改称为“写者”。
规定:允许多个读者同时读一个共享对象,但禁止读者、写者同时访问一个共享对象,也禁止多个写者访问一个共享对象,否则将违反Bernstein条件。
Bernstein条件:若两个程序P1和P2能满足下述条件,它们便能并发执行,否则不能: R(P1)∩W(P2)∪R(P2)∩W(P1)∪W(P1)∩W(P2)={},P1的读集与P2写集的交集、P2的读集与P1的写集的交集、P1的写集与P2的写集的交集,三者同时为空集。
通过描述可以分析,这里的读者和写者是互斥的,而写者和写者也是互斥的,但读者之间并不互斥。
由此我们可以设置3个变量,一个用来统计读者的数量,另外两个分别用于对读者数量读写的互斥,写者和读者、其他写者的互斥。

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <semaphore.h>
#include <windows.h>

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t rw = PTHREAD_MUTEX_INITIALIZER;
int count=0;

void* reader(void *)
{
    pthread_mutex_lock(&mutex);
    if(count==0) // 0个读者
        pthread_mutex_lock(&rw); // 等待写者退出
    ++count;
    pthread_mutex_unlock(&mutex);
    Sleep(1000);
    puts("stop reading.");
    pthread_mutex_lock(&mutex);
    --count;
    pthread_mutex_unlock(&mutex);
    if(count==0)
        pthread_mutex_unlock(&rw);
}

void* writer(void*)
{
    pthread_mutex_lock(&rw);
    Sleep(2000);
    puts("stop writing.");
    pthread_mutex_unlock(&rw);
}

int main()
{
    pthread_t t[6];
    for (int i = 0; i < 3; i++)
        pthread_create(&t[i],NULL,reader,NULL);
    pthread_t tt;
    pthread_create(&tt,NULL,writer,NULL);
    for (int i = 3; i < 6; i++)
        pthread_create(&t[i],NULL,reader,NULL);
    for (int i = 0; i < 6; i++)
        pthread_join(t[i],NULL);
    pthread_join(tt,NULL);
    return 0;
}

输出是
stop reading.
stop reading.
stop reading.
stop reading.
stop reading.
stop reading.
stop writing.
因为前三个读者读的过程中,写无法进行,只好等到所有读都完成后才能写。

3.哲学家进餐问题

问题描述:
有五个哲学家,他们的生活方式是交替地进行思考和进餐。哲学家们公用一张圆桌,周围放有五把椅子,每人坐一把。在圆桌上有五个碗和五根筷子,当一个哲学家思考时,他不与其他人交谈,饥饿时便试图取用其左、右最靠近他的筷子,但他可能一根都拿不到。只有在他拿到两根筷子时,方能进餐,进餐完后,放下筷子又继续思考。
根据问题描述,五个哲学家分别可以看作是五个进程,五只筷子分别看作是五个资源,只有当哲学家分别拥有左右的资源时,才得以进餐。如果不指定规则,当每个哲学家手中只拿了一只筷子时会造成死锁,从而五个哲学家都因为吃不到饭而饿死。因此我们的策略是让哲学家同时拿起两只筷子,需要对每个资源设置一个互斥量。

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <semaphore.h>
#include <windows.h>

#define PHILOSOPHER 5
pthread_mutex_t mutex[PHILOSOPHER];
// x号哲学家需要x和(x+1)%PHILOSOPHER号筷子

void* philosopher(void* pnum)
{
    int num = *(int*)pnum;
begin:
    if(pthread_mutex_trylock(mutex+num)==0)
    {
        if(pthread_mutex_trylock(mutex+(num+1)%PHILOSOPHER)!=0)
        {
            pthread_mutex_unlock(mutex+num);
            goto begin;
        }
        // 获得两只筷子
        printf("philosopher %d start eating.\n", num);
        Sleep(100);
        printf("philosopher %d stop eating.\n", num);
        pthread_mutex_unlock(mutex+num);
        pthread_mutex_unlock(mutex+(num+1)%PHILOSOPHER);
        Sleep(rand()%1000+2000); // 思考2到3秒
    }
    goto begin;
}

int main()
{
    int num[PHILOSOPHER];
    for(int i=0; i<PHILOSOPHER; ++i)
    {
        mutex[i] = PTHREAD_MUTEX_INITIALIZER;
        num[i] = i;
    }
    pthread_t t[PHILOSOPHER];
    for(int i=0; i<PHILOSOPHER; ++i)
        pthread_create(&t[i],NULL,philosopher,&num[i]);
    for(int i=0; i<PHILOSOPHER; ++i)
        pthread_join(t[i],NULL);
    return 0;
}

以上代码均在Windows下CodeBlocks 16.01编译通过
参考浅谈进程同步和互斥的概念

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值