Должен ли я использовать ManualResetEvent в качестве объекта блокировки? - PullRequest
4 голосов
/ 21 марта 2011

Приведенный ниже метод должен возвращать 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;
                    // /951548/pochemu-asinhronnyi-metod-delegata-trebuet-vyzova-endinvoke#951551
                    _onExpired.BeginInvoke(_originalAction, EndInvoke, null);
                }
            }
        }
    }
}

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

Ответы [ 4 ]

13 голосов
/ 21 марта 2011

Я бы пошел по другому пути ...

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
}   
2 голосов
/ 21 марта 2011

В настоящее время я только когда-либо блокирую () для простого объекта System.Object, который я создал только для блокировки.

Я бы определенно не блокировал () что-то вроде события, не потому чтоэто не сработает, но потому, что я думаю, что это может быть довольно странно использовать lock () для объекта, который (хотя и полностью отдельно) связан с операциями типа блокировки ядра.

Я не понимаю, что вы на самом деле делаете здесь, но это выглядит скорее как то, что названный Mutex мог бы сделать лучше.

1 голос
/ 21 марта 2011

Я думаю, что для этого лучше использовать 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 - Я все еще читаю об этом!


Еще одно примечание ... его фрагмент кода не совсем понятен, но если вы хотите внедрить глобальный синглтон, то решение 4 на http://www.yoda.arachsys.com/csharp/singleton.html - хорошее место для начала

0 голосов
/ 21 марта 2011

Единственное, что вам нужно убедиться, это то, что один и тот же объект, к которому вы привязаны, доступен для всех экземпляров кода, которые необходимо синхронизировать. Кроме этого, нет проблем.

...