通过生产者与消费者模型感受死锁——西邮本科生的实验

栏目: IT技术 · 发布时间: 4年前

内容简介:通过观察、分析实验现象,深入理解产生死锁的原因,学会分析死锁的方法, 并利用 pstack、 gdb 或 core 文件分析( valgrind (DRD+Helgrind) 可选 )其中的一 种方法来分析死锁。CPU:Intel i5

一. 实验目的及实验环境

1.实验目的

通过观察、分析实验现象,深入理解产生死锁的原因,学会分析死锁的方法, 并利用 pstack、 gdb 或 core 文件分析( valgrind (DRD+Helgrind) 可选 )其中的一 种方法来分析死锁。

2.实验环境    

(1)硬件

CPU:Intel i5

内存:16G

显示器:NVIDIA 1050Ti

硬盘空间:1T    

(2)软件  

虚拟机名称及版本:VMware

操作系统名称及版本:Ubuntu16.04

编译器:gedit

二. 实验内容

1、实验前准备工作  

仔细阅读参考资料 Linux 死锁现象及分析方法,了解对死锁现象进行分析的各种工具,选择其中一种对死锁现象进行分析。

2、实验内容

1)准备好生产者-消费者问题,或者哲学家就餐问题产生死锁的代码。

2)编译程序后,注意加调试选项-g,先预计一下这个程序的运行结果,运行程序,若程序没有响应,按 ctrl+c 中断程序运行,然后再重新运行,如此反复若干次,记录下每次的运行结果。若产生了死锁,通过 工具 对死锁进行分析。

三、实验结果分析

连续多次查看这个进程的函数调用关系堆栈,死锁线程将一直处于等锁状态,对比多次的函数调用堆栈输出结果,确定哪两个线程(或者几个线程)一直没有变化且一直处于等锁的状态,给出运行结果截图,在图中标出死锁出现的地方,并分析为什么会出现死锁代码设计:假设只有一个生产者,却有一个消费者,生产者一次生产一个资源,消费者一次消耗一个资源,按照基本原理应该是先申请资源,进而互斥锁上锁,若申请失败,就不上锁,等待申请成功,再上锁。为了产生死锁条件修改顺序:先互斥锁上锁,然后再进行资源申请。这样有可能出现生产者未来得及生产资源,消费者就进行申请,但先上锁后申请,所以未申请到,不会解锁,因为互斥锁未解锁,生产者无法生产。举个简单的例子(和我组成员刘传玺一同商讨得出):

假设有一个筐子,甲做馒头,乙吃馒头,合理的情况应该是乙看一眼筐里有没有馒头,若有,则伸手去取,若没有,则等甲放进去,再取;相对应,如果做甲看见乙在取馒头,此时筐子被占用了,甲暂时还不能放馒头进去,等乙取完了, 甲才放新馒头。就这样有条不紊一直运行。但是现在情况变了,乙不管三七二十一伸手就拿,要是拿到了还好,就吃了,要是手快了,馒头还没做好,他伸手取一抓,没抓找,手就放在筐子里等,甲一看手在筐里放着,我馒头也放不进去啊,那就等他把手拿出来再放进去... ...一个在等馒头来,一个在等手出去:死锁!

发生死锁,无资源,却申请资源

通过生产者与消费者模型感受死锁——西邮本科生的实验

进行检查

通过生产者与消费者模型感受死锁——西邮本科生的实验 锁定错误位置 :

通过生产者与消费者模型感受死锁——西邮本科生的实验

互斥锁先锁定后申请资源,顺序出错,可能会导致死锁。发现错误,解决错误:

通过生产者与消费者模型感受死锁——西邮本科生的实验

四、总结

平时阅读代码,觉得一切顺理成章,非常自然,从未思考为何要这样做。通过本次实验,老师逆向思维,让我们写出死锁!所有代码都在避开死锁,老师让我们写出死锁,无从下手,毫无头绪,实在让人头疼。查阅资料,反复理解运行顺序: 申请,上锁,释放,来来回回,费九牛二虎之力才写出死锁。回头观望, 瞬间恍然大悟,明白老师了良苦用心,躲避错误人人都会,但如果我能从无错中犯错,也就是说我理解了整个运行结构,操作流程之后,才能知道在何处会犯错, 能犯错,通过犯错让我们更深刻的体会错误,理解错误。从而根本的明白错误发生的原因以及修改的方式。不得不说实在高明。同时我也感受到了 Linux 代码的严谨,仅仅是两行代码顺序调换,就发生了 意想不到的错误,若在大工程中犯错,可能会带来毁灭性的后果。让我在感叹代码严谨的同时,也让我明白了不可以抱有侥幸心理,只有错和不错,没有可能一说!可能有错那就是错误,100%正确才是真正的正确,严谨认真、高效简洁是编写代码要有的思维风范。

五.附录:源代码(电子版)

#include <stdio.h>

#include <stdlib.h>

#include <unistd.h>

#include <pthread.h>

#include <semaphore.h>


#define M 1

#define P(x) sem_wait(&x)

#define V(x) sem_post(&x)


int in = 0;

int out = 0;

int buff[M] = {0};


sem_t sem_dr;

sem_t sem_co;

pthread_mutex_t mutex;


void print()

{

static int number = 0;

int i;

printf("(%2d)\t",number);

for(i = 0; i < M; i++)

printf("%d ", buff[i]);

number++;

printf("\n");

}


void *producer()

{

for(;;)

{

sleep(1);

P(sem_dr);

pthread_mutex_lock(&mutex);

in = in % M;

printf("(+)produce a product. buffer:");

buff[in] = 1;

print();

++in;

pthread_mutex_unlock(&mutex);

V(sem_co);

}

}


void *consumer()

{

for(;;)

{

sleep(1);

pthread_mutex_lock(&mutex);

P(sem_co);

out = out % M;

printf("(-)consume a product. buffer:");

buff[out] = 0;

print();

++out;

pthread_mutex_unlock(&mutex);

V(sem_dr);

}

}


void sem_mutex_init()

{

int init1 = sem_init(&sem_dr, 0, M);

int init2 = sem_init(&sem_co, 0, 0);

if( (init1 != 0) && (init2 != 0))

{

printf("sem init failed \n");

exit(1);

}

int init3 = pthread_mutex_init(&mutex, NULL);

if(init3 != 0)

{

printf("mutex init failed \n");

exit(1);

}

}


int main()

{

pthread_t id1;

pthread_t id2;

int i;

int ret;

sem_mutex_init(); /*create the producer thread*/

ret = pthread_create(&id1, NULL, producer, NULL);

if(ret != 0)

{

printf("producer creation failed \n");

exit(1);

}

ret = pthread_create(&id2, NULL, consumer, NULL);

if(ret != 0)

{

printf("consumer creation failed \n");

exit(1);

}

pthread_join(id1,NULL);

pthread_join(id2,NULL);

exit(0);

}


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

轻量级Django

轻量级Django

茱莉亚·埃尔曼 (Julia Elman)、马克·拉温 (Mark Lavin) / 侯荣涛、吴磊 / 中国电力出版社; 第1版 / 2016-11-1 / 35.6

自Django 创建以来,各种各样的开源社区已经构建了很多Web 框架,比如JavaScript 社区创建的Angular.js 、Ember.js 和Backbone.js 之类面向前端的Web 框架,它们是现代Web 开发中的先驱。Django 从哪里入手来适应这些框架呢?我们如何将客户端MVC 框架整合成为当前的Django 基础架构? 本书讲述如何利用Django 强大的“自支持”功......一起来看看 《轻量级Django》 这本书的介绍吧!

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具