第9章 线程编程(8)_死锁

5.6 死锁

(1)死锁:两个线程试图同时占用两个资源,并按不同的次序锁定相应的共享资源。

(2)解决方案:

  ①方案1:按相同的次序锁定相应的共享资源

  ②方案2:使用pthread_mutex_trylock(),它是pthread_mutex_lock()函数的非阻塞版。

【编程实验】死锁

//dead_lock.c

#include <pthread.h>
#include <errno.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>

typedef struct
{
    int value;
    pthread_mutex_t mutex;
}ResourceA;

typedef struct
{
    int value;
    pthread_mutex_t mutex;
}ResourceB;

typedef struct
{
    ResourceA*   ra;
    ResourceB*    rb;
}Storage;

void* a_fn(void* arg)
{
    Storage* s = (Storage*)arg;

    //先对ResourceA加锁
    pthread_mutex_lock(&s->ra->mutex);
    sleep(1);

    printf("0x%lx is waiting for ResourceB...\n", pthread_self());
    //再次ResourceB加锁
    pthread_mutex_lock(&s->rb->mutex);
    printf("ResourceA value is:%d\n", s->ra->value);
    printf("ResourceB value is:%d\n", s->rb->value);

    pthread_mutex_unlock(&s->rb->mutex);
    pthread_mutex_unlock(&s->ra->mutex);

    return (void*)0;
}

void* b_fn(void* arg)
{
   /*死锁
    Storage* s = (Storage*)arg;

    //先对ResourceB加锁
    pthread_mutex_lock(&s->rb->mutex);
    sleep(1);

    printf("0x%lx is waiting for ResourceA...\n", pthread_self());
    //再次ResourceA加锁
    pthread_mutex_lock(&s->ra->mutex);
    printf("ResourceA value is:%d\n", s->ra->value);
    printf("ResourceB value is:%d\n", s->rb->value);

    pthread_mutex_unlock(&s->ra->mutex);
    pthread_mutex_unlock(&s->rb->mutex);
    */

    //解决方案:与a线程一样,按相同的次序加锁
    Storage* s = (Storage*)arg;

    //先对ResourceA加锁
    pthread_mutex_lock(&s->ra->mutex);
    sleep(1);

    printf("0x%lx is waiting for ResourceB...\n", pthread_self());
    //再次ResourceB加锁
    pthread_mutex_lock(&s->rb->mutex);
    printf("ResourceA value is:%d\n", s->ra->value);
    printf("ResourceB value is:%d\n", s->rb->value);

    pthread_mutex_unlock(&s->rb->mutex);
    pthread_mutex_unlock(&s->ra->mutex);

    return (void*)0;
}

int main(void)
{
    ResourceA  ra;
    ResourceB  rb;
    ra.value = 100;
    rb.value = 200;
    pthread_mutex_init(&ra.mutex, NULL);
    pthread_mutex_init(&rb.mutex, NULL);
    Storage s = {&ra, &rb};

    int err = 0;
    pthread_t   thread_a, thread_b;
    if((err = pthread_create(&thread_a, NULL, a_fn, (void*)&s)) !=0){
        fprintf(stderr, "pthread_create:%s\n", strerror(errno));
        exit(1);
    }
    if((err = pthread_create(&thread_b, NULL, b_fn, (void*)&s)) !=0){
        fprintf(stderr, "pthread_create:%s\n", strerror(errno));
        exit(1);
    }

    pthread_join(thread_a, NULL);
    pthread_join(thread_b, NULL);

    pthread_mutex_destroy(&ra.mutex);
    pthread_mutex_destroy(&rb.mutex);

    return 0;
}
/*
 0xb6d08b70 is waiting for ResourceB...
 ResourceA value is:100
 ResourceB value is:200    //死锁时,停在这个位置
 0xb7709b70 is waiting for ResourceB...
 ResourceA value is:100
 ResourceB value is:200    //未发生死锁时会输出到这里
 */
时间: 2024-12-30 00:27:13

第9章 线程编程(8)_死锁的相关文章

第9章 线程编程(4)_线程同步1:互斥锁

5. 线程的互斥和同步 5.1 同步和互斥的概念 (1)线程同步:是一个宏观概念,在微观上包含线程的相互排斥和线程的先后执行的约束问题.解决同步方式一般采用条件变量和信号量. (2)线程互斥:线程执行的相互排斥(注意,它不关心线程间执行的先后顺序!).解决互斥一般使用互斥锁.读写锁和信号量. [编程实验]银行ATM(线程不安全的例子) //account.h #ifndef __ACCOUNT_H__ #define __ACCOUNT_H__ typedef struct { int code

《Java并发编程实战》第二章 线程安全性 读书笔记

一.什么是线程安全性 编写线程安全的代码 核心在于要对状态访问操作进行管理. 共享,可变的状态的访问 - 前者表示多个线程访问, 后者声明周期内发生改变. 线程安全性 核心概念是正确性.某个类的行为与其规范完全一致. 多个线程同时操作共享的变量,造成线程安全性问题. * 编写线程安全性代码的三种方法: 不在线程之间共享该状态变量 将状态变量修改为不可变的变量 在访问状态变量时使用同步 Java同步机制工具: synchronized volatile类型变量 显示锁(Explicit Lock

[Java并发编程之美]第1章 线程基础(待更新)

第1章 线程 线程与进程 进程是操作系统资源分配和调度的基本单位,但cpu资源是分配到线程的,也就是线程是CPU分配的基本单位. 线程自己的栈资源中,存放的局部变量是线程私有的,其他线程无法访问,除此之外栈还存线程的调用栈帧. 线程创建 三种方式:实现Runnable接口的run方法:继承Thread类并重写run方法:使用FutureTask方式. 线程等待与通知 1 wait() 线程先要事先获得共享变量上的监视器锁,然后当一个线程调用一个共享变量的wait()方法,该线程会被阻塞挂起,并且

[CSAPP笔记][第十二章并发编程]

第十二章 并发编程 如果逻辑控制流在时间上是重叠,那么它们就是并发的(concurrent).这种常见的现象称为并发(concurrency). 硬件异常处理程序,进程和Unix信号处理程序都是大家熟悉的例子. 我们主要将并发看做是一种操作系统内核用来运行多个应用程序的机制. 但是,并发不仅仅局限于内核.它也可以在应用程序中扮演重要的角色. 例如 Unix信号处理程序如何允许应用响应异步事件 例如:用户键入ctrl-c 程序访问虚拟存储器的一个未定义的区域 其他情况 访问慢速I/O设备 当一个应

第十二章 并发编程

第十二章 并发编程 三种基本的构造并发程序 进程:每个逻辑控制流是一个进程,由内核进行调度,进程有独立的虚拟地址空间 I/O多路复用:逻辑流被模型化为状态机,所有流共享同一个地址空间 线程:运行在单一进程上下文中的逻辑流,由内核进行调度,共享同一个虚拟地址空间 常用函数: fork exec waitpid 基于I/O多路复用的并发事件驱动服务器 事件驱动程序:将逻辑流模型化为状态机. 状态机: 状态 输入事件 转移 对于状态机的理解,参考EDA课程中学习的状态转换图的画法和状态机. 整体的流程

第二章线程同步基础

Java 7 并发编程实战手册目录 代码下载(https://github.com/Wang-Jun-Chao/java-concurrency) 第二章线程同步基础 2.1简介 多个执行线程共享一个资源的情景,是最常见的并发编程情景之一.在并发应用中常常遇到这样的情景:多个线程读或者写相同的数据,或者访问相同的文件或数据库连接. 为了防止这些共享资源可能出现的错误或数据不一致,我们必须实现一些机制来防止这些错误的发生. 为了解决这些问题,引入了临界区(Critical Section)概念,临

《Programming in Go》第七章并发编程译文

中文译名 Go 编程 外文原文名 Programming in Go 外文原文版出处 the United States on recycled paper at RR Donnelley in Crawfordsville, Indiana. 译 文: 第七章 并发编程 7.1主要概念 7.2例子 7.2.1:过滤器 7.2.2:并发查找 7.2.3:线程安全表 7.2.4:Apache 报告 7.2.5:找重复 并发编程能够让开发者实现并行算法,以及利用多处理器和多核写程序.但是在大多主流变

iOS线程编程指南

原英文网址为: https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/Multithreading/ThreadSafety/ThreadSafety.html 同步 在应用程序中的多个线程的存在开辟了潜在的问题,关于安全访问到资源从多个执行线程.两个线程修改相同的资源可能会相互干扰,以意想不到的方式.例如,一个线程可能会覆盖其他人的更改或应用程序置于未知和潜在无效的状态.如果你很幸运,已损坏的资源可能会导致

第三章 网络编程

终于学到网络编程了! 先上图和程序: 这是今天写的TCP的实现 服务器和客户端分别在两台电脑 这是服务器图: 这是服务器程序: 1 #-*- coding:utf-8 -*- 2 from socket import * #导入socket所有属性 3 from time import ctime #导入ctime() 4 5 6 host = '' #HOST 变量为空,表示bind()函数可以绑定在所有有效的地址上. 7 port = 21000 #设置端口 8 bufsize = 1024