当前位置:C++技术网 > 资讯 > 原子操作与互斥访问关系分析

原子操作与互斥访问关系分析

更新时间:2015-06-24 23:53:25浏览次数:1+次

    原子操作,重点在于操作,然后才是修饰的特性“原子”。操作说的是,一个动作,就是一系列指令语句组成的一个功能动作,比如赋值,运算,最后输出。这三个步骤可以看成一个操作,也可以看成是三个操作。这里要强调的是,我们是谈动作,而不是某一个数据。只要完成了语句的执行,操作就完成了,这就是操作,一个单纯的动作。为什么我要这么说呢?因为很多人在思考这个问题时,总是将操作和数据混在一起考虑。这里就是想把你的思维先单纯化,先只看到操作。所以,到这里,你不要想其他的,知道操作是什么就行。操作就是执行语句。

    原子操作,最基础的意思就是,要么执行完毕,要么就不执行。请大家不要小瞧这几个字。就这么几个字,我才琢磨清楚。反而各种说法容易偏离主旨。原子操作意思就是把你要执行的操作当作原子一样,不可以再分。当然,原子操作在内部是可以分成多个步骤,成千上万条语句都可以。只不过,原子操作假借原子来形象的比喻这一系列操作要像原子一样,不可以再分,这只是一个人为的规定,是一个逻辑抽象的概念。虽然可以很多条语句,但是你得保证,要么全部执行完毕,要么一个也不执行。虽然就这么几个字的规定,但是蕴含大量信息。

    下面来仔细解释原子操作的这几个字的规定。

    原子操作,要么一个也不执行,要么全部执行。在数据库系统中,有一个事务的概念,其实就是原子操作。事务的机制,通过日志记录,保证了原子操作的特性。要么全部执行成功,如果执行几个语句后失败,执行过的语句必然对数据库有影响,但是没有执行完,会导致数据错乱。所以数据库通过日志倒着执行一次,就回滚了操作,保证了这个原子操作特性。从这里你也可以看到,原子操作并不是一定是只能执行一遍,要么成功,要么不执行。但是,不执行你又怎么能百分百保证一定会失败呢?所以数据库系统就通过日志记录保证可以恢复原样。我们可以把数据库的事务理解为尝试执行,失败则回滚,成功就完成。这一套机制不只是执行了一遍,还有回滚的保证,整个这个过程,保证了原子操作。

    然而,在程序中,我们也可以通过日志式实现原子操作,我们也经常用。但是这并不是最高效的。而在操作系统中,给我们提供了原子操作的支持。这里并没有使用日志式的。所以,如何实现原子操作呢?不要不加思考的说什么同步互斥内核对象来实现,这样说你还没有完全理解原子操作,只能说,你会用了而已。

    既然不能用日志式,那我们就在执行前做好充分准备,要么就一次性全部成功,要么就不要开始执行。如何保证,这时就用到了互斥访问的特性。互斥访问就是对公共的资源独占访问,你占用了其他人就不能用,其他人用了你就不能用,只有等别人用完了才可以使用。就是利用这个互斥特性,在执行操作前,你独占了互斥对象(这里是一个通用的说法,可以指同步对象,也可以是互斥对象),其他人就不能占用,就只有等你用完了才能使用。而你在使用这个互斥对象时,就只是用来把别人挡在门口而已。然后你的操作就可以不受干扰的执行完,执行完后别人才可以执行。这样你就可以不中断的执行。

    以上就是互斥访问在原子操作中起的作用,是用来实现原子操作的一个前提基础。事实上,你可能还会忽略一点。在现代操作系统中,如windows都是多任务系统,一个线程可以代表一个任务,分配cpu时间也是以线程为单位。为了公平,线程是轮流使用cpu的。这样导致线程用完给定的时间后,会被切换到其他线程执行。这样切换就可能打破原子操作特性。因为线程被切换后,其他线程运行,就可能更改你要操作的变量,从而导致再切换回来,变量值就不是你预想中的那样了。

    而使用互斥对象,其他线程也必须使用同样的方法来操作,这样就遵守互斥访问的规则执行。因为你的线程已经占用了互斥对象,即使你的线程被切换走了,其他线程执行,但是访问互斥对象时,无法访问,就阻止了进一步操作,从而保证你的线程的数据依然在你的预料之中。切回来后继续执行,没有任何问题。这是互斥对象的作用,当然你作为一个程序员,必须保证你要执行的原子操作的变量,都要按照这个方式来执行,即执行前先得到互斥对象。如果得不到,就等待。这就是满足,要么不执行,要么执行完毕。

    其实,执行完毕还隐藏另一个意思。就是说,在执行的过程中,不要中断了。如果中断了,那么执行就没有完毕,或者执行不能卡死在里面。否则就破坏了原子操作特性,而后果就是,你的线程执行不正常,导致其他相关线程全部无法正常执行。如果你在原子操作内部,即得到互斥对象和最后执行完所有操作后释放互斥对象中间的操作请求别人得到的互斥对象,那就形成了死锁,是多线程最头痛的问题。

    所以,对于原子操作,务必要保证,要么不执行,要么就执行完毕。所以,在中间过程中,千万小心使用同步互斥对象。而一开始的互斥对象只是用来实现原子操作的。因为这里不像数据库用日志记录,那样效率很低。这里才有执行前检测的机制实现。最终能不能很好的实现原子操作要看使用的程序员了。

    以上的解释不知道你是否清楚了原子操作和互斥访问之间的关系。还有不清楚的话,请留言。