Created
January 27, 2014 07:56
-
-
Save JeffreyZhao/8644613 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 其实这题可以写的很简单,它的关键根本不是CAS操作。 | |
// 使用一个标志(flag也好,status判断也罢)来避免Start和Dispose重入是很容易 | |
// 想到的手段,很多同学也这么做了。大部分同学遇到的问题是:假如StartCore已经 | |
// 启动,那么如何让Dispose方法等待其完成。有些同学用while (true),有些同学用 | |
// 锁,但都避免不了让Dispose线程等待Start线程。 | |
// 这里“避免等待”的关键在于要跳出思维界限:谁说DisposeCore方法一定要在Dispose | |
// 方法里执行的?我们就不能在Start里销毁对象吗? | |
public class FineLockCalculator : CalculatorBase { | |
private enum Status { | |
NotStarted, | |
Starting, | |
Started, | |
Disposed | |
} | |
private readonly object _gate = new object(); | |
private Status _status = Status.NotStarted; | |
public virtual void Start() { | |
lock (_gate) { | |
if (_status != Status.NotStarted) | |
return; | |
_status = Status.Starting; | |
} | |
StartCore(); | |
lock (_gate) { | |
if (_status == Status.Starting) { | |
_status = Status.Started; | |
return; | |
} | |
} | |
DisposeCore(); | |
} | |
public void Dispose() { | |
lock (_gate) { | |
var origStatus = _status; | |
_status = Status.Disposed; | |
if (origStatus != Status.Started) | |
return; | |
} | |
DisposeCore(); | |
} | |
} | |
// 在Dispose方法中,我们对_status进行标记,但只在确定Started的情况下才 | |
// 调用DisposeCore。同理,在Start方法中假如发现_status已经变成了Disposed, | |
// 则原地调用DisposeCore销毁对象。使用这种方法,Dispose方法完全不需要做 | |
// 任何等待,整体等待时间极短。 | |
// 清晰起见,上述代码没有使用任何CAS操作,只是对于_status读写加以简单保护。 | |
// 假如需要的话,我们也可以把三个lock轻松修改为CAS操作,但这只是起到锦上 | |
// 添花的作用。 | |
// 有些同学一上来就搞CAS,然后发现问题之后只是简单的修补,于是代码越变越 | |
// 复杂。理清思路,划分好边界,处理好边界上的竞争关系,其实逻辑说到底就是 | |
// 这么简单. |
@JeffreyZhao 弱弱的问一句,在Start()的最后一句为什么是DisposeCore()而不是Dispose()呢?在这里直接调用DisposeCore()是不是意味着,有个前提的假设是基类里实现的DisposeCore()可以被多次重复调用呢?
@dawnbreaks: 没仔细看,但为什么会那么复杂,按照我的代码改写成CAS的,应该十分简单才对。
@ivenxu: 当然应该调用DisposeCore
,再仔细看一下代码,DisposeCore
不会被重复调用。
Nice approach. The key point here is that thinking outside of box, let DisposeCore being called from Start().
@JeffreyZhao, 仔细看了,DisposeCore()确实不大可能被重复调用.再弱弱的多问一句,DisposeCore()在Start()中在什么样的场景才会被调用?我实在想不出来,因为在我看来在Start()里面执行完_status = Status.Started;就return;了。
@ivenxu Start()里面的DisposeCore()还是有可能会被调用的,比如正在执行StartCore()的时候调用了Dispose(),那么_status = Status.Disposed,然后StartCore()执行完毕后会调用DisposeCore(). 以上只是个人粗浅的理解。
又学了一手,太机智了。
不过仔细想想,这样对于调用者来说太trick了吧。比如我发现dispose结束了,心想太好了可以拔电源/拔网线了,但其实DisposeCore可能还没开始呢。
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@JeffreyZhao 帮忙review下纯CAS版本,无锁。