• 操作系统 线程同步 基础知识 (一)


    6.2 临界区问题

    临界区问题的解答必须满足以下三项要求:

    1. 互斥. 不能有两个线程同时在临界区内执行

    2. 前进. 当临界区为空, 而一个线程希望进入临界区时, 该线程进入临界区

    3. 有限等待. 一个线程从申请进入临界区到真正进入临界区这段时间不能无限长.

    6.3 Peterson 算法

    一种软件的方法解决死锁问题, 但也并不能总是解决死锁问题

    定义两个变量, 分别为 flag[0/1], turn. flag 表示哪个线程有进入临界区的意愿, turn 表示哪个线程

    while(true) {
    	flag[i] = true;
    	turn = j;
    	
    	while(flag[j] && turn == j); // let j run first
    	// cirtical section
    
    	flag[i] = false;
    	// remainer section
    }
    

    证明满足上面 3 个要求

    1. 互斥. 假设两个线程同时进入临界区, 那么 flag[i] == flag[j] == 1. 当 turn = j 时, 线程 i 在自旋, 不可能进去.

    2. 前进. 当线程 j 没有意愿进入临界区时, flag[j] = false; 当 i 有意愿进去时, 直接就进入了.

    3. 有限等待. 首先申请临界区的线程进入临界区, 一个线程至多等待另一个线程在临界区执行一次, 满足有限等待.

    6.4 硬件同步.

    使用原子函数 setAndSwap()

    void getAndSet(bool var) {
    	swap(lock, var);
    }
    

    上面的代码核心是 getAndSet 必须时原子操作. 当 lock 为 false 时, 线程上锁进入临界区. 当 lock 为 true 时, 那么 getAndSet 无限循环, 直到 lock 为 false;

    6.5 信号量与死锁

    acquire() {
    	value --;
    	if(value < 0) {
    		add this process to list
    		block;
    	}
    }
    
    release() {
    	value ++;
    	if(value <= 0) {
    		remove a process P from list
    		wakeup(P);
    	}
    }
    

    当 value 为负时, value 的绝对值对应被阻塞线程的数目.

    死锁

    S.acquire();         Q.acquire();
    Q.acquire();         S.acquire();
    
    ...			...
    
    S.release();         Q.release();
    Q.release();         S.release();
    

    上面代码中, P0 执行 S.acquire(), P1 执行 Q.acquire, 然后 P0 执行 Q.acquire, 最后 P1 S.acquire.  

    然后... 就死锁了

    6.6 经典同步问题

    1. 有限缓冲区问题

    生成者通过 insert 函数向缓冲区内添加 item, 消费者通过 remove 函数在缓冲区内删除 item

    信号量有 3 个, 分别为 empty, full, mutex. empty 表示缓冲区内的空格位置, insert 时需要检查缓冲区是否为空, remove 时需要检查缓冲区是否含有元素. mutex 提供对缓冲区的互斥访问

    semaphore empty, full, mutex;
    
    // init empty = size, full = 0, mutex = 1;
    
    // java code
    public void insert(Object item) {
    	empty.acquire();
    	mutex.acquire();
    
    	buffer[in] = item;
    	in = (in+1) % BUFFER_SIZE;
    
    	mutex.release();
    	full.release();
    }
    
    public Object remove() {
    	full.acquire();
    	mutex.acquire();
    
    	Object item = buffer[out];
    	out = (out + 1) % BUFFER_SIZE;
    
    	mutex.release();
    	empty.release();
    
    	return item;
    }
    

      

    注意, empty, full 都必须使用信号量来表示其剩余个数, 不能用 if 代替

    2. 读者写者问题

    // writer may starve
    
    void read() {
    	while(true) {
    		mutext.acquire();
    		readCount ++;
    		if(readCount == 1) {
    			writeLock.acquire();
    		}
    		mutext.release();
    
    		do reading
    
    		mutext.acquire();
    		readCount --;
    		if(readCount == 0) {
    			writeLock.release();
    		}
    		mutext.release();
    	}
    }
    
    void write() {
    	writeLock.acquire();
    	do writing
    	writeLock.release();
    }
    

      

    6.7 管程

    管程将 acquire, release 这些操作封装起来了. 管程确保一次只有一个进程能在管程内活动.

    管程内的条件遍历有两个操作:

    wait() 挂起调用进程并释放管程, 直至另一个进程在条件变量上执行 signal()

    signal() 假如有因条件变量被挂起的线程, 那么释放之, 否则什么也不做.

    管程实现生产者消费者问题

    // full means that no space for new added item
    // empty means that no item for consumer
    monitor ProducerConsumer {
    	int itemCount;
    	condition full;
    	condition empty;
    
    	procedure add(item) {
    		if(itemCount == BUFFER_SIZE) {
    			wait(full);
    		}
    
    		putItemIntoBuffer(item);
    		itemCount ++;
    
    		if(itemCount == 1) {
    			signal(empty);
    		}
    	}
    
    	procedure remove() {
    		if(itemCount == 0) {
    			wait(empty);
    		}
    
    		removeItemFromBuffer();
    		itemCount --;
    
    		if(itemCount == BUFFER_SIZE-1)
    			signal(full);
    	}
    }
    

    管程示意图

  • 相关阅读:
    寒江独钓(0):内核开发上机指导
    异常:Hibernate数据库恢复错误
    天书夜读:从汇编语言到Windows内核编程笔记(4)
    企业WEBGIS网站解决方案
    如何使用国际开源项目构建一个完整的GIS(地理信息)应用系统
    将指定文件夹下的所有文件copy到目标文件夹下
    一些jquery的小知识
    压缩指定目录下指定文件(包括子目录下的文件)
    解压一个rar文件
    关于下载txt文本文挡的问题
  • 原文地址:https://www.cnblogs.com/zhouzhuo/p/3627139.html
Copyright © 2020-2023  润新知