Skip to content

Instantly share code, notes, and snippets.

@JeffreyZhao
Created January 27, 2014 07:56
Show Gist options
  • Save JeffreyZhao/8644613 to your computer and use it in GitHub Desktop.
Save JeffreyZhao/8644613 to your computer and use it in GitHub Desktop.
// 其实这题可以写的很简单,它的关键根本不是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,然后发现问题之后只是简单的修补,于是代码越变越
// 复杂。理清思路,划分好边界,处理好边界上的竞争关系,其实逻辑说到底就是
// 这么简单.
@yhliaoluan
Copy link

又学了一手,太机智了。
不过仔细想想,这样对于调用者来说太trick了吧。比如我发现dispose结束了,心想太好了可以拔电源/拔网线了,但其实DisposeCore可能还没开始呢。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment