线程与锁
例子:可能的交换
考虑一个类,它具有类变量a和b并且方法hither和yon:
现在假如这样的两个线程被建立,并且一个被命名为hither而另一个被命名为yon。那么,什么是活动的请求集合,并且什么是其约束的顺序? 让我们考虑线程highter。根据规则,这个线程必须引发一个b的use活动,并且被在a的assign(赋值)所跟随。这是执行一个调用方法hither的最小请求开销。 现在,线程的变量b第一个活动不能是use。但是可以是assign或load。因为程序中没有调用这样的一个assign,所有一个对b的assign(赋值)不能发生,所有一个b的load活动被请求。线程的load活动轮流通过主存请求一个对于变量b的预先的read活动。 线程可以随意地sotre(存储)在assign(赋值)后所的值。如果这作了,那么store活动轮流通过主存请求一个随其后的write活动。 这些情况对称为yon的线程也是一样的,但是a与b的角色相互交换了。 所有的活动可以以下图所描述: (见图1) 这里的从A到B的一个箭头表明A必须领先于B。 主存储器所生产的活动顺序是什么样的呢?这个唯一的约束就是不可能a的write活动领先与a的read活动并且b的write活动领先于b的read活动,因为这个在图表中的因果关系的箭头形成了一个循环,以至于一个活动必须领先于其自身,而这是不可以的。假定store和write活动随意地产生,有三种可能的顺序使主存储器能够合理地执行活动。设ha和hb为a和b对于hither线程的工作区拷贝,设ya和yb是yon线程的工作区拷贝,并设ma和mb是主存储器的主拷贝。初始化ma=1并且mb=2。那么会有三种可能性作为这些活动的顺序:
因此在主存中的最后结果可能就是:b被复制到a,a被复制到b,或者a与b的值被交换了;些外,变量的工作区拷贝有可能不会相同。这将导致不正确,当然,假定这些结果之一都比另一个有希望的话。这是一个程序的行为必须依赖于时间的地方。 当然,一个实现当然可以选择不去引发store和write活动,或者只引发这一双中的一个,导致另外的一些可能的结果。 现在让我们修改这个例子来使use的同步方法:
让我们再一次考虑线程hither。根据规则,这个线程必须引发一个lock活动(在类SynchSample类的实例上hither被调动时)在hither的方法实体被运行之前。这是在b的use之后,并且a的assign之后。最后,在执行hither方法的实体之后,SynchSample类的的实例unlock活动会被引发。这是调用执行hither方法需要的最小需求。 在些之后,b的load被请求,并由主存储器轮流请求一个前置运行的read活动。因为load跟随lock活动,而相应的read也必须跟随lock活动。 因为unlock活动跟随a的assign,而store活动是强制的,它通过主存储器轮流请求后置运行的write活动。Write活动必须领先于unlock活动。 对于线程yon的情况是一样的,但是a与b的角色会被互换。 所有的活动可以用以下图来刻画: lock和unlock活动提供了进一步通过主存强制约束活动的顺序;而一个线程的lock活动在其它线程的lock和unlock活动之间不可被引发。此外,unlock活动要求store和write活动引发。它可能有两种情况:
当结束状态依靠于时间,执行的结果可能会一致。 例子:不遵守顺序的wirtes(写) 这个例子是相似于以前的一些章节的,除了一个方法对两个变量赋值,并且另一个方法读两个变量。考虑一个类,它有类变量a和b并且有方法to和fro:
现在假如这两个线程被建立,并且称为to 而另一个线程则称为fro。那么它们所请求的活动集体是什么呢?而它们的执行顺序又是怎样约束的呢? 让我们考虑这个称为to的线程。这个线程必须引发一个被b的assign活动所跟随的a的assign。这是运行方法to最基本的需要。因为这里没有同步,实现是否将被赋予的值存入内存是随意的!因此称为fro的线程可能获得的a是1或是3,并且可能获得的b是2或是4。 现在假设这个to被同步,而fro没有:
|