我正在尝试实现一个基本的Future
类(是的,我知道有Task
,但这是为了教育目的),并遇到了Monitor
类的奇怪行为。该类的实现方式是在构造函数中进入锁定状态,将退出锁定的操作排队到线程池。结果获取器检查实例变量以查看操作是否已完成,如果未完成,则进入锁定状态,然后返回结果。问题在于,实际上结果获取器不等待排队的操作完成,而是继续进行,导致结果不正确。以下是代码。
// The class itself
public class Future<T>
{
private readonly Func<T> _f;
private volatile bool _complete = false;
private T _result;
private Exception _error = new Exception("WTF");
private volatile bool _success = false;
private readonly ConcurrentStack<Action<T>> _callbacks = new ConcurrentStack<Action<T>>();
private readonly ConcurrentStack<Action<Exception>> _errbacks = new ConcurrentStack<Action<Exception>>();
private readonly object _lock = new object();
public Future(Func<T> f)
{
_f = f;
Monitor.Enter(_lock);
ThreadPool.QueueUserWorkItem(Run);
}
public void OnSuccess(Action<T> a)
{
_callbacks.Push(a);
if (_complete && _success)
a(_result);
}
public void OnError(Action<Exception> a)
{
_errbacks.Push(a);
if (_complete && !_success)
a(_error);
}
private void Run(object state)
{
try {
_result = _f();
_success = true;
_complete = true;
foreach (var cb in _callbacks) {
cb(_result);
}
} catch (Exception e) {
_error = e;
_complete = true;
foreach (var cb in _errbacks) {
cb(e);
}
} finally {
Monitor.Exit(_lock);
}
}
public T Result {
get {
if (!_complete) {
Monitor.Enter(_lock);
}
if (_success) {
return _result;
} else {
Console.WriteLine("Throwing error complete={0} success={1}", _complete, _success);
throw _error;
}
}
}
// Failing test
public void TestResultSuccess() {
var f = new Future<int>(() => 1);
var x = f.Result;
Assert.AreEqual (1, x);
}
我正在使用Mac OS X 10.9上的Mono 3.2.3。
Task<T>
/TaskCompletionSource<T>
吗?因为那会简单得多... - Marc GravellFuture
只在其构造函数中锁定一次。 - VladResult
访问器:它进入了。 - Marc Gravell