并发进程的正确性

  • 独立进程
  1. 不和其他进程共享资源或状态
  2. 确定性 -> 输入状态决定结果
  3. 可重现 -> 能够重现起始条件
  4. 调度顺序不重要
  • 并发进程
  1. 在多个进程间有资源共享
  2. 不确定性
  3. 不可重现
  • 并发进程的正确性

  • 执行过程是不确定性和不可重现的

  • 程序错误可能是间歇性发生的

并发的好处

  • 共享资源
  • 加速
  • 模块化

同步问题

时间 A B
3:00 查看冰箱,没有面包
3:05 离开家去商店
3:10 到达商店 查看冰箱,没有面包了
3:15 购买面包 离开家去商店
3:20 到家,把面包放进冰箱 到达商店
3:25 购买面包
3:30 到家,把面包放进冰箱

解决

利用两个原子操作实现一个锁(lock)

Lock.Acquire()

在锁被释放前一直等待,然后获得锁

如果两个线程都在等待同一个锁,并且同时发现锁被释放了,那么只有一个能够获得锁

breadlock.Acquire();    //进入临界区
if (nobread) {
buy bread; //临界区
}
breadlock.Release(); //退出临界区

进程的交互关系:相互感知程度

相互感知的程度 交互关系 进程间的影响
相互不感知(完全不了解其它进程的存在) 独立 一个进程的操作对其他进程的结果无影响
间接感知(双方都与第三方交互,如共享资源) 通过共享进行协作 一个进程的结果依赖于共享资源的状态
直接感知(双方直接交互,如通信) 通过通信进行协作 一个进程的结果依赖于从其他进程获得的信息

临界区(Critical Section)

  • 进程中访问临界资源的一段需要互斥执行的代码
  • 检查可否进入临界区的一段代码
  • 如可进入,设置相应"正在访问临界区"标志
  • 如可进入,设置相应"正在访问临界区"标志

实现方法

  • 禁用中断

进入临界区,禁止所有中断,并保存标志,离开临界区,使能所有中断,并恢复标志,没有中断,没有上下文切换,因此没有并发

但是禁用中断后,进程无法被停止,整个系统都会为此停下来,可能导致其他进程处于饥饿状态

  • 基于软件的同步解决方法

Peterson算法实现

do {
flag[i] = true;
turn = j;
while ( flag[j] && turn == j); CRITICAL SECTION flag[i] = false; REMAINDER SECTION } while (true);
  • 更高级的抽象方法
  1. 锁(lock)

锁是一个抽象的数据结构,一个二进制变量(锁定/解锁),Lock::Acquire(),锁被释放前一直等待,然后得到锁,释放锁,唤醒任何等待的进程

  1. 原子操作指令

测试和置位(Test-and-Set )指令,从内存单元中读取值,测试该值是否为1(然后返回真或假),内存单元值设置为1

boolean TestAndSet (boolean *target)‏
{
boolean rv = *target;
*target = true;
return rv:
}

交换指令(exchange)

 void Exchange (boolean *a, boolean *b)‏
{
boolean temp = *a;
*a = *b;
*b = temp:
}

使用TS指令实现自旋锁(spinlock)

Lock::Acquire() {
while (test-and-set(value))
; //spin
}

信号量

信号量是操作系统提供的一种协调共享资源访问的方法,软件同步是平等线程间的一种同步协商机制,OS是管理者,地位高于进程,用信号量表示系统资源的数量

信号量是一种抽象数据类型

由一个整形 (sem)变量和两个原子操作组成

P() (Prolaag (荷兰语尝试减少))

  • sem减1
  • 如sem<0, 进入等待, 否则继续

V() (Verhoog (荷兰语增加))

  • sem加1
  • 如sem≤0,唤醒一个等待进程
classSemaphore {
int sem;
WaitQueue q;
} Semaphore::P() {
sem--;
if (sem < 0) {
Add this thread t to q;
block(p);
}
} Semaphore::V() {
sem++;
if (sem<=0) {
Remove a thread t from q;
wakeup(t);
}
}

用信号量实现临界区的互斥访问

必须成对使用P()操作和V()操作

mutex = new Semaphore(1);

mutex->P();
Critical Section;
mutex->V();

用信号量实现条件同步

condition = new Semaphore(0);

A:
condition->P(); B:
condition->V();

生产者-消费者问题

Class BoundedBuffer {
mutex = new Semaphore(1);
fullBuffers = new Semaphore(0);
emptyBuffers = new Semaphore(n);
} BoundedBuffer::Deposit(c) {
emptyBuffers->P();
mutex->P();
Add c to the buffer;
mutex->V();
fullBuffers->V();
} BoundedBuffer::Remove(c) {
fullBuffers->P();
mutex->P();
Remove c from buffer;
mutex->V();
emptyBuffers->V();
}

管程(Moniter)

管程是一种用于多线程互斥访问共享资源的程序结构,采用面向对象方法,简化了线程间的同步控制,任一时刻最多只有一个线程执行管程代码,正在管程中的线程可临时放弃管程的互斥访问,等待事件出现时恢复

组成

  • 一个锁

    控制管程代码的互斥访问

  • 0或者多个条件变量

    管理共享数据的并发访问

条件变量

  • 条件变量是管程内的等待机制

    进入管程的线程因资源被占用而进入等待状态,每个条件变量表示一种等待原因,对应一个等待队列

  • Wait()操作

    将自己阻塞在等待队列中,唤醒一个等待者或释放管程的互斥访问

  • Signal()操作

    将等待队列中的一个线程唤醒,如果等待队列为空,则等同空操作

生产者和消费者问题

classBoundedBuffer {

Lock lock;
int count = 0;
Condition notFull, notEmpty;
} BoundedBuffer::Deposit(c) {
lock->Acquire();
while (count == n)
notFull.Wait(&lock);
Add c to the buffer;
count++;
notEmpty.Signal();
lock->Release();
} BoundedBuffer::Remove(c) {
lock->Acquire();
while (count == 0)
notEmpty.Wait(&lock);
Remove c from buffer;
count--;
notFull.Signal();
lock->Release();
}

管程实现哲学家就餐问题

typedef struct monitor{
semaphore_t mutex; // 二值信号量,只允许一个进程进入管程,初始化为1
semaphore_t next; //配合cv,用于进程同步操作的信号量
int next_count; // 睡眠的进程数量
condvar_t *cv; // 条件变量cv
} monitor_t; typedef struct condvar{
semaphore_t sem; //用于发出wait_cv操作的等待某个条件C为真的进程睡眠
int count; // 在这个条件变量上的睡眠进程的个数
monitor_t * owner; // 此条件变量的宿主管程
} condvar_t;

管程中的条件变量cv通过执行wait_cv,会使得等待某个条件C为真的进程能够离开管程并睡眠,且让其他进程进入管程继续执行;而进入管程的某进程设置条件C为真并执行signal_cv时,能够让等待某个条件C为真的睡眠进程被唤醒,从而继续进入管程中执行。发出signal_cv的进程A会唤醒睡眠进程B,进程B执行会导致进程A睡眠,直到进程B离开管程,进程A才能继续执行,这个同步过程是通过信号量next完成的;而next_count表示了由于发出singal_cv而睡眠的进程个数。

void
cond_signal (condvar_t *cvp) {
cprintf("cond_signal begin: cvp %x, cvp->count %d, cvp->owner->next_count %d\n", cvp, cvp->count, cvp->owner->next_count);
if(cvp->count>0) { //当前存在睡眠的进程
cvp->owner->next_count ++;//睡眠的进程总数加一
up(&(cvp->sem));//唤醒等待在cv.sem上睡眠的进程
down(&(cvp->owner->next));//把自己睡眠
cvp->owner->next_count --;//睡醒后等待此条件的睡眠进程个数减一
}
cprintf("cond_signal end: cvp %x, cvp->count %d, cvp->owner->next_count %d\n", cvp, cvp->count, cvp->owner->next_count);
}

将指定条件变量上等待队列中的一个线程进行唤醒,并且将控制权转交给这个进程

void
cond_wait (condvar_t *cvp) {
//LAB7 EXERCISE1: YOUR CODE
cprintf("cond_wait begin: cvp %x, cvp->count %d, cvp->owner->next_count %d\n", cvp, cvp->count, cvp->owner->next_count);
cvp->count++;//需要睡眠的进程个数加一
if(cvp->owner->next_count > 0)
up(&(cvp->owner->next));//唤醒进程链表中的下一个进程
else
up(&(cvp->owner->mutex));//否则唤醒睡在monitor.mutex上的进程
down(&(cvp->sem));//将自己睡眠
cvp->count --;//睡醒后等待此条件的睡眠进程个数减一
cprintf("cond_wait end: cvp %x, cvp->count %d, cvp->owner->next_count %d\n", cvp, cvp->count, cvp->owner->next_count);
}

函数的功能为将当前进程等待在指定信号量上,其操作过程为将等待队列的计数加1,然后释放管程的锁或者唤醒一个next上的进程来释放锁,然后把自己等在条件变量的等待队列上,直到有signal信号将其唤醒,正常退出函数

void phi_take_forks_condvar(int i) {
down(&(mtp->mutex)); //通过P操作进入临界区
state_condvar[i]=HUNGRY; //记录下哲学家i是否饥饿,即处于等待状态拿叉子
phi_test_condvar(i);
while (state_condvar[i] != EATING) {
cprintf("phi_take_forks_condvar: %d didn't get fork and will wait\n",i);
cond_wait(&mtp->cv[i]);//如果得不到叉子就睡眠
}
//如果存在睡眠的进程则那么将之唤醒
if(mtp->next_count>0)
up(&(mtp->next));
else
up(&(mtp->mutex));
} void phi_put_forks_condvar(int i) {
down(&(mtp->mutex));//通过P操作进入临界区 state_condvar[i]=THINKING;//记录进餐结束的状态
phi_test_condvar(LEFT);//看一下左边哲学家现在是否能进餐
phi_test_condvar(RIGHT);//看一下右边哲学家现在是否能进餐
//如果有哲学家睡眠就予以唤醒
if(mtp->next_count>0)
up(&(mtp->next));
else
up(&(mtp->mutex));
}

最后的实现

Lab7:同步互斥的更多相关文章

  1. ucore操作系统学习(七) ucore lab7同步互斥

    1. ucore lab7介绍 ucore在前面的实验中实现了进程/线程机制,并在lab6中实现了抢占式的线程调度机制.基于中断的抢占式线程调度机制使得线程在执行的过程中随时可能被操作系统打断,被阻塞 ...

  2. ucore lab7 同步互斥机制 学习笔记

    管程的设计实在是精妙,初看的时候觉得非常奇怪,这混乱的进程切换怎么能保证同一时刻只有一个进程访问管程?理清之后大为赞叹,函数中途把前一个进程唤醒后立刻把自己挂起,完美切换.后一个进程又在巧妙的时机将自 ...

  3. 入门级的按键驱动——按键驱动笔记之poll机制-异步通知-同步互斥阻塞-定时器防抖

    文章对应视频的第12课,第5.6.7.8节. 在这之前还有查询方式的驱动编写,中断方式的驱动编写,这篇文章中暂时没有这些类容.但这篇文章是以这些为基础写的,前面的内容有空补上. 按键驱动——按下按键, ...

  4. 经典线程同步 互斥量Mutex

    阅读本篇之前推荐阅读以下姊妹篇: <秒杀多线程第四篇一个经典的多线程同步问题> <秒杀多线程第五篇经典线程同步关键段CS> <秒杀多线程第六篇经典线程同步事件Event& ...

  5. (转)经典线程同步 互斥量Mutex

    阅读本篇之前推荐阅读以下姊妹篇: <秒杀多线程第四篇一个经典的多线程同步问题> <秒杀多线程第五篇经典线程同步关键段CS> <秒杀多线程第六篇经典线程同步事件Event& ...

  6. 总结windows多线程同步互斥

    windows多线程同步互斥--总结 我的windows多线程系列文章: windows多线程--原子操作 windows多线程同步--事件 windows多线程同步--互斥量 windows多线程同 ...

  7. 多线程面试题系列(7):经典线程同步 互斥量Mutex

    前面介绍了关键段CS.事件Event在经典线程同步问题中的使用.本篇介绍用互斥量Mutex来解决这个问题. 互斥量也是一个内核对象,它用来确保一个线程独占一个资源的访问.互斥量与关键段的行为非常相似, ...

  8. windows多线程同步互斥--总结

    我的windows多线程系列文章: windows多线程--原子操作 windows多线程同步--事件 windows多线程同步--互斥量 windows多线程同步--临界区 windows多线程同步 ...

  9. windows多线程同步--互斥量

    关于互斥量的基本概念:百度百科互斥量 推荐参考博客:秒杀多线程第七篇 经典线程同步 互斥量Mutex 注意:互斥量也是一个内核对象,它用来确保一个线程独占一个资源的访问.互斥量与关键段的行为非常相似, ...

  10. 秒杀多线程第七篇 经典线程同步 互斥量Mutex

    本文转载于:http://blog.csdn.net/morewindows/article/details/7470936 前面介绍了关键段CS.事件Event在经典线程同步问题中的使用.本篇介绍用 ...

随机推荐

  1. IdentityService4学习笔记之Authorization Code

    前文 本文所有内容来自官方文档,如果有写不明白的地方请下方留言或查看官方文档. 今天介绍Authorization Code模式,适用于保密类型的客户端,保密类型客户端可以理解为在服务器端生成页面(比 ...

  2. dotnet core 之 gRPC

    dotnet core gRPC 原文在本人公众号中,欢迎关注我,时不时的会分享一些心得 HTTP和RPC是现代微服务架构中很常用的数据传输方式,两者有很多相似之处,但是又有很大的不同.HTTP是一种 ...

  3. 记一次CSS反爬

    目标网址:猫眼电影 主要流程 爬取每一个电影所对应的url 爬取具体电影所对应的源码 解析源码,并下载所对应的字体 使用 fontTools 绘制所对应的数字 运用机器学习的方法识别对应的数字 在源码 ...

  4. 开发技术--Numpy模块

    开发|Numpy模块 Numpy模块是数据分析基础包,所以还是很重要的,耐心去体会Numpy这个工具可以做什么,我将从源码与 地产呢个实现方式说起,祝大家阅读愉快! Numpy模块提供了两个重要对象: ...

  5. vue+element打印页面功能

    项目中遇到了要打印页面的功能,我感之前使用的是一个第三方的插件,但是不兼容IE,后来发现直接调用window的API就可以了,MMP // 打印表单 printBtn() { window.print ...

  6. 个人项目-wc

    个人项目-WC   (C语言) 一.Github地址:https://github.com/Lin-J-F/WC 二.PSP表格 PSP2.1 Personal Software Process St ...

  7. java全套学习资料

    1.背景 技术需要大家的共同努力,在这里我将平时学习过的觉得比较好的资料分享给大家; 当然,最好的学习就是输出,与大家分享,在分享的资料中有的是自己的总结,有的是分享来自互联网,如果有侵权请联系删除; ...

  8. Zabbix监控多个JVM进程

    一.场景说明:   我们这边的环境用的是微服务,每个程序都是有单独的进程及单独的端口号,但用jps查询出来的结果有些还会有重名的情况,所以某些脚本不太适用本场景: 二.需求说明: 需使用Zabbix- ...

  9. Linux shell变量详解

    Shell 是一个用 C 语言编写的程序,它是用户使用 Linux 的桥梁.Shell 既是一种命令语言,又是一种程序设计语言. Shell 是指一种应用程序,这个应用程序提供了一个界面,用户通过这个 ...

  10. 3. 上网调查一下目前流行的源程序版本管理软件和项目管理软件都有哪些, 各有什么优缺点? (提示:搜索一下Microsoft TFS、GitHub、Trac、Bugzilla、Rationale,Apple XCode),请用一个实际的源代码管理工具来建立源代码仓库,并签入/签出代码。

    上网调查一下目前流行的源程序版本管理软件和项目管理软件都有哪些, 各有什么优缺点? ---------------答题者:徐潇瑞 (1)Microsoft TFS的优缺点: 优点:是对敏捷,msf,c ...