• CSAPP笔记(第十二章 并发编程02)


    p716~p757, 分两次, 716~733, 733~757.

    摘要

    本章后半部分主要讨论了线程并发下的安全问题. 如何保证多线程下能程序能"正确"的执行.

    线程内存模型

    一组并发线程运行在一个进程的上下文中.
    独享的: 线程ID, 栈, 栈指针, 程序计数器, 条件码, 通用目的寄存器值.
    共享的: 用户虚拟地址空间(包括 代码, 读/写数据, 堆以及所有的共享库代码和数据区域), 打开文件的集合.

    变量映射到内存

    • 全局变量, 在虚拟内存的读/写区域, 所有线程共享一个实例.
    • 本地自动变量, 定义在函数内部但没有static属性的变量. 运行时, 每个线程的栈都包含它自己的所有本地自动变量的实例.
    • 本地静态变量, 定义在函数内部有static属性的变量, 全局共享一个实例.

    信号量

    大神Edsger Dijkstra提出了"信号量"(semaphore)的特殊变量来解决并发的问题.

    信号量s是具有非负整数值的全局变量, 只能由两种特殊的操作来处理, 分别为P操作和V操作.

    • P(s): if(s > 0) s = s -1, return s; if(s = 0) wait;
    • V(s); s = s +1; 唤醒等待s的众多线程中的一个去执行P操作.
    #include <semaphore.h>
    
    int sem_init(sem_t *sem, 0, unsigned int value);
    int sem_wait(sem_t *s); /* P(s) */
    int sem_post(sem_t *s); /* V(s) */
    

    使用信号量的示例代码

    volatile long cnt = 0;
    sem_t mutex;
    sem_init(&mutex, 0, 1);
    for (int i=0; i< niters; i++){
      sem_wait(&mutext);
      cnt++;
      sem_post(&mutext);
    }
    

    使用多线程完成任务

    使用多线程执行一个任务时, 注意将任务拆分为独立的子任务, 让各个线程独立执行, 然后将各自的结果合并为一个最终结果. 尽量少用P, V操作, 因为P, V操作开销较大, 导致程序运行缓慢.

    线程安全

    4种线程不安全类型:

    1. 不保护共享变量的函数. 在对共享变量操作前后没有P, V操作导致, 变量执行不对.
      解决办法: 加上P, V可以很容易的解决这个问题, 但会导致程序速度减慢.
    2. 保持跨越多个调用的状态的函数.
      解决办法: 重写该函数, 不使用static数据, 依靠调用者传递参数.
    3. 返回指向静态变量的指针的函数.
      解决办法1: 重写函数, 使得调用者传递存放结果的变量的地址.
      解决办法2: 使用加锁-复制(lock-and-copy)技术, 加锁, 复制到私有地址, 解锁.
    4. 调用线程不安全函数的函数.
      解决办法: 如果调用的函数g是第2种情况, 则调用方f也是不安全的, 除了修改g没有别的办法. 如果是1, 3情况, 则可以通过加锁的方式解决问题.

    可重入性

    线程安全函数中有一种函数, 叫做可重入函数(reentrant function). 当被多个线程调用时, 不会引用任何共享数据. 这个函数内部没有引用共享数据, 所以不需要加锁, 因而效率更高.

    让我联想到"纯函数"的概念, 纯函数有2个条件

    1. 不依赖外部数据, 只依赖传入的参数
    2. 不产生可观察的副作用(包括I/O, 修改传入参数, 读取/修改共享变量等)

    这样看, 纯函数肯定是可重入的. 类似的概念还有"接口的幂等性", "无状态服务".

    日常开发当中, 应该尽量多写一些纯函数, 这样修改和重构才会更加安全.

    有些编程新手写的代码中, 混入了很多共享变量, 多次函数中都对共享变量进行了修改, 后来运行结果莫名其妙, 还找了半天找不到问题的原因. 看代码的人也看不懂原作者的意图, 真是害人不浅.

  • 相关阅读:
    code review
    设计原则
    知识点介绍
    REST API
    第四章 模块化React和Redux应用
    第3章 从Flux到Redux
    第二章 设计高质量的React组件
    React和Jquery比较
    第一章 React新的前端思维方式
    封装一个获取module.exports内容的方法
  • 原文地址:https://www.cnblogs.com/winwink/p/CSAPP_Note_Chapter12_Concurrency_02.html
Copyright © 2020-2023  润新知