linux多线程-操作系统线程同步互斥

这一目主要我想得是理论和实际结合的办法去做,先将理论,把这块在操作系统中的内容先进行陈述。然后用linux下的代码去真正实现。

Perterson算法

理论

Perterson算法是用来是实现对临界区资源的互斥访问,它是用软件的机制实现。也就是说在linux系统编程当中,如果不让你使用pthread_mutex_t mutex你怎么解决这个问题。Perterson算法给出了软件实现的方法。

伪代码

bool flag[] = {false, false}; // flag[i]表示进程i是否想进入临界区,true表示想进入
int turn; // turn = i,表示可进入临界区的进程是i

void P0(){

    while(true){

        flag[0] = true; // 自己想进入
        turn = 1;       // 但是,礼让对方

        while( flag[1] && 1==turn ); // 此时符合P1的执行条件,让进程1执行,进程0此时忙等待

        /*
         *critical region
         * */

        flag[0] = false;

    }
}
void P1(){

    while(true){

        flag[1] = true;
        turn = 0;

        while( flag[0] && 0==turn );

        /*
         *critical region
         * */

        flag[1] = false;
    }

}

来分析一下上面这段代码,如果因为turn作为公共资源,并且对它本省不加锁,所以,必然有一个后访问它的进程,假设是P1.那就是P0先访问,P1紧接着就访问,也就是说其实他们两都想访问。但是P0比P1稍微快一点,此时P0进来之后,先表示自己想进临界区,然后让对方先进turn=1。P1比P0慢一点,此时P1也想进,那么flag[1]=true;;turn=0;注意,在turn=0之前,由于P0先执行了,所以P0一直在忙等待,此时turn=0之后,P0进入临界区,由于turn保存的是较晚的一次赋值,所以,如果turn=0。表示P1是后进来的,那么此时P0先进去执行即可。

如果,不存在均要求进入的情况,那就简单了。

生产者-消费者问题

理论

这一块,主要是参考了本科时的操作系统课本。具体的内容就不多说了,给出伪代码我觉得已经很清晰了。


#define N 128        // 缓冲区大小即缓冲池中缓冲区个数
int buffer[N];       // 缓冲池
int front = 0;       // 队列头
int rear = 0;        // 队列尾下一个

semaphore mutex = 1; // 互斥信号量
semaphore full = 0;  // 同步信号量 - 满缓冲区个数
semaphore empty = N; // 同步信号量 - 空缓冲区个数

void producer(){

    P(empty);

    produce an item;

    P(mutex);
    buffer[rear] = item;
    rear = (rear+1)%N;
    V(mutex)

    V(full)

}

void consumer(){

    P(full);

    P(mutex);
    item = buffer[front];
    front = (front+1)%N;
    V(mutex);

    consume an item;

    V(empty);
}

/*
当然,严格的代码中,semaphore是结构体。
上面的代码只是便于理解。
 * */
typedef list_of_process LP; // 这种类型没有,暂时先这么写
typedef semaphore{
    int value;
    LP L; // 资源的进程阻塞队列

}semaphore_t;

void P( semaphore_t& S ){ // atomically
    S.value = S.value - 1;
    if( S.value < 0 )
        block(S.L);
}
void V( semaphore_t& S ){ // atomically
    S.value = S.value + 1;
    if(S.value <= 0)
        wakeup(S.L);
}

补充:这么搞,在真实写代码的时候有问题吧。虽然生产者和消费者访问的是不同的单元,但是对于队列的操作。生产者和消费者可能会有问题吧。一定好把问题分析清楚,这到底是不是读者-写者问题?不是!!!因为,访问的不是同一个单元。

新的认识
之前对于上面的问题认识的不够透彻,其实我最近在linux下面写的代码不能算是生产者-消费者。也不是读者-写者。它就是两个写者,那锁去控制互斥,信号量控制同步就可以了。
但是,上面的代码,我已经分析清楚了。不是读者-写者,因为访问的并不是同一个变量。没有读-写的矛盾。

上面的问题就是经典的生产者-消费者问题。只不过我的问题是,你的mutex是干嘛的?
现在想清楚了,就是加在整个队列上面的。
因为我之前的理解不是这样,我认为如果是单生产者和单消费者,是不需要mutex的,因为生产者和消费者本质没有访问同一个变量。所以,他们之间只有同步的语义。互斥的语义是加在生产者与生产者,消费者与消费者之间的。这么想确实没有错,因为不同的生产者要申请单元,不能两个生产者申请同一个单元。所以,他们之间需要互斥。消费者同理。

所以,我当初认为,单个生产者单个消费者,和多个生产者多个消费者的区别就在于,前者没有同类对象之间的锁。但是,现在,这种想法是不对的。其实,基本上也没问题。

考虑这样的一个场景:对于一个队列,有一个生产者,有一个消费者。好了,现在,队列中只有一个元素。

同时来了生产者和消费者,注意,队列假设用链表实现。此时,队列中只有一个元素。设当前这个节点的指针为p,对于生产者而言,它要在p的后面追加一个元素,而消费者要释放p。此时,两个代码都执行了,假设消费者稍快于生产者,释放了p。而此时生产者由于已经过了判断队列是否为空的情形,任然执行p->next = q;那么,显然,程序会出错!
这就是问题,起码这个是我想到的。对于生产者和消费者而言,队列上面要加一把锁。这个锁可以避免同一类对象申请资源,也可以避免不同对象对于队列的修改。但是,这样也会带来问题,就是生产的时候不能消费,消费的时候不能生产。按理说,他们两的逻辑是没关系的。

哲学进餐问题

读者-写者问题

问题描述:读者-写者是这样的一个问题。允许多个线程同时读,但是,但是,只允许一个线程写。也就是说,如果一个线程在写,那么其他线程不能写也不能读。但是,如果一个线程在读,其他线程可以读,但是不能写。

问题分析:考虑读者-写者之间用互斥实现,这样可以避免读者-写者之间的互斥。此时,读的时候不能写,写的时候不能读。但是,这么做会导致,读的时候也不能读了。所以,这是问题。在读者之间还需要实现一定程度的共享。

此时,可以考虑给读者一个计数器,当这个计数器不为0的时候,其他读者只需修改计数器的值,然后去读就好了。不用等待读者和写者之间的锁。然后读取操作结束,修改计数器的值即可。在计数器值为0的时候,需要申请和释放。显然,不同读者之间对于这个计数器是共享的,所以也需要互斥访问。

semaphore_t rmutex = 1;
semaphore_t wmutex = 1;

void reader(){

    P(rmutex);
    if(readcount == 0) P(wmutex);
    readcount++;
    V(rmutex);

    /* read operation */

    P(rmutex);
    readcount--;
    if(readcount == 0) V(wmutex);
    V(rmutex);

}

void writer(){
    P(wmutex);
    /* write operation */
    V(wmutex);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值