• 为什么要同步


    8.1、为什么要同步

    线程之间是合作关系。既然是合作,那就要由某种约定的规则,否则合作就会出现问题。

    例子1

    有两个线程同时运行,第一个线程在执行了一些操作后想检查当前的错误状态errno,但在其做检查之前,线程2却修改了errno。这样,当第一个线程再次获得控制权后,检查结果将是线程2改写过的errno,而这是不正确的。

    线程1 线程2
    。。。。。 。。。。。
    读errno变量 。。。。。
    。。。。。 写errno变量
    从读操作返回 。。。。
    检查errno值 。。。。


    一个进程的两个线程因为操作不同步而造成线程1运行错误。

    之所以出现上述问题,是基于两个原因:

    l  errno是线程之间共享的全局变量

    l  线程之间的相对执行顺序是不确定的

    因此,要解决上述问题有两个办法,即分别消除上述两个原因。消除第1个原因的办法就是限制全局变量,给每个线程一个私有的errno变量。事实上,如果可以将所有的资源都私有化,让线程之间不共享,那么这种问题就不复存在。

    但问题是,如果所有资源都不共享,那么还有必要发明线程吗?甚至也没有必要发明进程了。因为这样就违背了进程和线程设计的初衷:共享资源、提高资源利用率。因此,这种解决办法是不切实际的。

    那剩下的办法就是消除第2个原因,即让线程之间的相对执行顺序在需要的时候可以确定。

    例子2

    有两个线程A和B,分别执行指令x=1和x=2,即

    线程A:x=1;

    线程B:x=2;

    请问在上述两个线程结束后,x的值是什么?

    由于x是共享变量,且线程之间的相对执行顺序是不确定的,因此线程A先执行,也可以是B先执行。

    线程A先执行,那么,x=2;

    线程B先执行,那么,x=1。

    还有别的结果吗?x有可能等于3吗?

    一般情况下,在任何计算机体系结构中x=1对应的不是一条微指令,即一条高级指令对应的是多条微指令,因此一条高级指令需要多个时钟周期(通常为6个时钟周期以上) 才能完成。

    例如,x=1的赋值语句就是由多个步骤构成。这些步骤可能包括:先把总线清零,然后把1加到上面去。这样的话,线程A和线程B的执行可能形成如下穿插:线程A把总线清零,线程B把总线清零,线程A将数值1加到总线上,线程B将2加到总线上。这样就由可能出现结果3。

    当然了,出现x=3的结果依赖于特定的指令集结构。如果指令集结构在执行赋值语句时不是先将总线清零,然后将要赋值的常数加到总线上,就不会出现结果3,而只能有结果1或者2。

    就算赋值语句可以完整(不分割)的执行,编译器的优化也有可能造成不可预料的结果。例如,设定x、y两个变量的初始值为0,进程中的两个线程执行的指令如下:

    线程A:r2=x;y=1;

    线程B:r1=y;x=2;

    该程序运行的结果时什么呢?

    r1=0,r2=0是第1种结果;

    r1=0,r2=2是第2种结果;

    r1=1,r2=0是第3种结果;

    r1=1,r1=2是第4种结果。那么,第4种结果会出现吗?

    看上去似乎不可能,因为线程A和线程B的指令无论如何穿插,此种结果也不可能出现。遗憾的是,这种推论没有考虑到编译器优化的影响。

    由于编译器在进行优化时可以(其实是通常)改变语句的顺序,有可能导致两个线程执行的实际指令顺序如下:

    线程A:y=1,r2=x

    线程B:x=2,r1=y

    而此时,如果两个线程的指令执行进行穿插的话,结果就有可能是r1=1,r2=2了。

     

    例子3

    线程A

    线程B

    i=0;

    while(i<10)

    {

      i++

    }

    printf “A finished”

    i=0

    while(i>-10)

    {

      i--

    printf “B finished”

    请问,哪个线程会赢呢?即哪个线程会先运行结束?或者说,有没有哪个线程肯定会赢?答案是不确定的。

    如果两个线程恰好是你一步、我一步的执行的话,则两个线程都将无法结束。事实上,如果不采取特殊措施,就没有办法确保谁会赢,也没办法确保是否会结束。

    由上述例子可见,引入线程后,也引入了一个巨大的问题,即多线程程序的执行结果有可能是不确定的。而不确定则是我们人类非常反感的东西。那么,如何在保持线程这个概念的同时消除其执行结果的不确定性呢?答案是线程的同步。

  • 相关阅读:
    数组(Array)
    js数据类型自动转化规律
    ES6-12.Symbol
    彻底搞懂prototype和__proto__
    API测试利器——Postman(1. 安装和启动)
    全国各城市的代码邮编sql(mysql版)
    SQL执行的顺序
    jQuery $.each用法
    使用maven工具对maven项目进行打包所出现的问题
    关于Notepad++中用正则表达式匹配中文的问题
  • 原文地址:https://www.cnblogs.com/lanyuejiagou/p/12602800.html
Copyright © 2020-2023  润新知