5

下面的方法应该在第一次调用时返回 true,对任何其他调用返回 false。

有什么问题吗?使用重置事件进行锁定是否安全?

private ManualResetEvent _resetEvent = new ManualResetEvent(false);

public bool AmIFirst()
{
    lock (_resetEvent)
    {
        bool first = !_resetEvent.WaitOne(0);
        if (first)
            _resetEvent.Set();

        return first;
    }
}

编辑:在查看您的评论后,我做了一些更改。ManualResetEvent由于以前的设计理念,我被困住了。我其实根本不需要它。

class ActionSynchronizer
{
    private Timer _expirationTimer;
    private object _locker = new object();
    private bool _executionRequired = true;

    private SomeDelegate _onExpired = delegate { };

    public ActionSynchronizer(SomeDelegate onExpired)
    {
        _onExpired = onExpired;
        expirationTimer = new Timer(OnExpired, null, 30000, Timeout.Infinite);
    }

    public bool IsExecutionRequired()
    {
        if (!_executionRequired)
            return false;

        lock (_locker)
        {
            if (_executionRequired)
            {
                _executionRequired = false;
                return true;
            }

            return false;
        }
    }

    private void OnExpired(object state)
    {
        if (_executionRequired)
        {
            lock (_locker)
            {
                if (_executionRequired)
                {
                    _executionRequired = false;
                    // http://stackoverflow.com/questions/1712741/why-does-asynchronous-delegate-method-require-calling-endinvoke/1712747#1712747
                    _onExpired.BeginInvoke(_originalAction, EndInvoke, null);
                }
            }
        }
    }
}

// ...
{
    if (_action.Sync.IsExecutionRequired())
        _action.Invoke();
}
4

4 回答 4

14

我会在这里走不同的路线...

private int counter;
...
if(Interlocked.Increment(ref counter) == 1)
{
     // yes, I'm first
}

线程安全,无锁。或者,如果您担心环绕 Int32:

if(Interlocked.CompareExchange(ref counter, 1, 0) == 0)
{
     // yes, I'm first
}   
于 2011-03-21T09:28:19.847 回答
2

如今,我只在一个简单的 System.Object 对象上使用 lock(),该对象是我为锁定而创建的。

我绝对不会 lock() 像事件这样的东西,不是因为它不起作用,而是因为我认为在它本身(尽管完全分开)关联的对象上使用 lock() 可能会相当混乱内核锁定类型的操作。

我不清楚你在这里实际上在做什么,但它看起来更像是一个名为 Mutex 可能会做得更好的东西。

于 2011-03-21T09:21:59.293 回答
1

我认为最好在对象上使用 lock() 。

此外,您可以通过使用“双重检查锁定”来防止过多的线程锁定

例如

private object _protection = new object();
private bool _firstTime = true;

public bool AmIFirst()
{
    if (!_firstTime)
        return false;

    lock (_protection)
    {
        if (!_firstTime)
            return false;

        _firstTime = false;
        return true;
    }
}

注意... - 有一些关于双重​​检查锁定的有趣评论 - .NET 中的双重检查锁定- 我仍在阅读这个!


另一个注意事项......从您发布的代码片段中不清楚,但如果您希望实现全局单例,那么http://www.yoda.arachsys.com/csharp/singleton.html上的解决方案 4是一个好地方开始

于 2011-03-21T09:14:04.627 回答
0

您唯一需要确保的是,您锁定的同一个对象可以被所有需要同步的代码实例访问。除此之外,没有问题。

于 2011-03-21T09:16:10.593 回答