Monitor似乎没有锁定对象

时间:2013-11-28 12:24:54

标签: c# .net multithreading mono

我正在尝试实现一个基本的Future课程(是的,我知道Task但这是出于教育目的)并且遇到了Monitor类的奇怪行为。实现该类以便它在构造函数中进入锁定,将一个退出锁定的动作排队到一个线程池。结果getter检查实例变量以查看操作是否已完成,如果不是,则输入lock然后返回结果。问题是,实际上结果getter不会等待排队的操作完成并继续进行导致不正确的结果。这是代码。

// 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。

1 个答案:

答案 0 :(得分:6)

只有采取锁定的线程才能退出锁定。调用线程的构造函数中的Enter在完成时不能从线程池中Exit - 线程池工作者 没有锁

反过来说:可能是创建未来访问getter的同一个线程:允许再次访问Enter:它是重新进入。此外,您需要ExitEnter相同的次数,否则它实际上不会被释放。

基本上,我不认为Monitor是正确的方法。