zoukankan      html  css  js  c++  java
  • 线程安全

    《程序员的自我修养》-读书笔记

    线程

      程序执行流的最小单元,一个标准的线程由 线程ID、当前指令指针、寄存器集合和堆栈组成。线程依附于进程执行,一个进程由一个到多个线程组成。

    线程和进程的关系如下图:

      

     在单处理器应对多个线程的情况下,并发是一种模拟出来的状态,操作系统会让这些线程程序轮流执行,每次执行一小段时间,这样看起来每个线程在同时执行。

    这种行为称之为线程调度。在线程调度中,线程通常拥有至少三种状态:

    • 运行: 此时线程正在执行。
    • 就绪: 此时线程可以立刻运行,但是CPU已经被占用
    • 等待: 此时线程正在等待磨一时间(通常是I/O 或同步)发生,无法执行

    上述三种状态的转换如下图所示:

     线程被CPU调度一般取决于线程的优先级,线程优先级改变一般有三种方式:

     linux 线程

    Linux 将所有的执行实体(无论是线程还是进程〉都称为任务(Task ),每一个任务概念上都类似于一个单线程的进程,具有内存空间、执行实体、文件资源等.不过, Linux下不同的任务之间可以选择共享内存空间, 因而在实际意义上, 共享了同一个内存空间的多个任务构成了一个迸程,这些任务也就成了这个进程里的线程。在linux下,用以下方法可以创建一个新的任务:

     fork并不会复制父进程的内存空间,而是和原任务共享一个写时复制的内存空间,所以fork产生新任务的速度非常快。写时复制,顾名思义,就是子进程可以和父进程一起同时读取一块内存空间,但是一旦有任务需要对内存进行修改,那么内存就会复制一份提供给修改万单独使用,以免影响到其他的任务使用。

     线程安全

     多个线程同时访问一个共享数据,可能造成很恶劣的和后果。

    下面是一著名例子:

     ++操作的过程:1、读取i的值到寄存器X,2、X++, 3、将X的内容存回i

     原子性:一个操作不会被打断的执行完成。单指令的操作成为“原子的”

    操作系统提供了一些数据类型的原子操作,比如

     在复杂的场合下,比如我们需要保证一个复杂的数据结构更改的原子性,上述的原子操作指令就力不从心了。因此我们需要更加通用的手段:

    同步与锁

    所谓同步,既是指在一个线程访问数据未结束的时候, 其他线程不得对同一个数据进行访问。

    同步最常用的方式:

    • 二元信号量:最简单的锁,只有两种状态,占用和非占用。适合只能被唯一一个线程独占访问的资源。
    • 信号量:支持多个线程并发访问资源。一个初始值为N 的信号量允许N 个线程并发访问。线程访问资源的时候首先获取信号景, 进行如下操作:

    • 互斥量:和二元信号量很类似,资源仪同时允许一个线程访问, 但和信号量不同的是, 信号是在整个系统可以被任意线程获取并释放,也就是说,同一个信号量可以被系统中的一个线程获取之后由另一个线程释放。而互斥量则要求哪个线程,获取了互斥量,哪个线程就要负责释放这个锁,其他线程越组代庖去释放互斥量是无效的。
    • 临界区:把l临界区的锁的获取称为进入临界区, 而把锁的释放称为离开临界区。临界区和互斥量与信号量的区别在于, 互斥量和信号量在系统的任何进程里都是可见的,临界区的作用范围仅限于本进程,其他的进程无法获取该锁。除此之外,临界区具有和互斥量相同的性质。
    • 读写锁:致力于一种更加特定的场合的同步,对于多线程读取频繁,而仅仅偶尔写入的情况,如果采用上述同步方式,将显得低效。该写锁可以避免这个问题。对于向一个锁,读写锁有两种获取方式,共享的(Share)或独占的( Exclusive )。读写锁的特性可以描述为下面的表格:

    •  条件变量:可以有多个线程可以等待条件变量,如果条件变量还没唤醒。线程也可以唤醒条件变量,此时等待该条件变量的所有进程都会被唤醒继续执行,也就是说,使用条件变量可以让许多线程一起等待某个事件的发生,当事件发生时(条件变量被唤醒〉,所有的线程可以一起恢复执行.

    可重入和线程安全

    一个函数要被可重入,只有两种情况:

    1、多个线程同时执行这个函数

    2、函数自身调用自身

    一个函数被称为可重入的,表明该函数被重入之后不会有任何不良后果。一个函数要成为可重入,必须具有一下几个特点:】

     这里对于“不依赖任何单个资源的锁”这句话不是怎么理解。

    过度优化

    编译器可能会对一些变量的取值进行优化,比如取变量的值是从变量的实际地址处获取并将该值保存在寄存器中,第二次获取该变量的值就会直接从寄存器获取了。

  • 相关阅读:
    【1】排行榜算法设计
    基础问答【二】
    基础问答【一】
    【1】c语言
    (五)帧同步与状态同步
    (四)c++虚函数详解
    (三)git pull报错解决方案,Your local changes to the following files would be overwritten by merge
    (二)干货!获取该目录下,指定权限不为770的文件, 并设置权限为770
    【8】java新特性,双冒号 :: 的使用场景
    go(01) 基础语法
  • 原文地址:https://www.cnblogs.com/qinghaowusu/p/12891533.html
Copyright © 2011-2022 走看看