Как долго будет ждать блокировка C #, и что если код завершится сбоем во время блокировки? - PullRequest
46 голосов
/ 18 мая 2011

я видел следующий код, и я хотел использовать его для простого действия, которое может выполняться только по одному за раз и не будет происходить часто (поэтому вероятность появления дважды за один раз очень мала, ноВы никогда не знаете).

Итак, код:

 //class variable
    private static object syncRoot = new object();

    //in a method:
    lock (syncRoot)
    {
     DoIt();
    }

, когда другой поток приходит и хочет выполнить код, как долго он будет ждать, пока не будет снята блокировка?навсегда, или вы можете как-то установить время ожидания?

и второе: если метод DoIt() выдает исключение, блокировка все еще снята?

Ответы [ 7 ]

73 голосов
/ 18 мая 2011

когда другой поток приходит и хочет выполнить код, как долго он будет ждать, пока не будет снята блокировка?

lock заблокирует поток, пытающийся войти в блокировку на неопределенный срок, пока не будет освобожден заблокированный объект.

Можете ли вы как-нибудь намочить тайм-аут?

Если вам нужно указать время ожидания, используйте Monitor.TryEnter, как в

if(Monitor.TryEnter(obj, new TimeSpan(0, 0, 1))) {
    try {
        body 
    }
    finally {
        Monitor.Exit(obj);
    }
}

если метод DoIt () выдает исключение, блокировка все еще снята?

Да, lock(obj) { body } переводится как

bool lockWasTaken = false;
var temp = obj;
try { Monitor.Enter(temp, ref lockWasTaken); { body } }
finally { if (lockWasTaken) Monitor.Exit(temp); }

Подробную информацию о том, что может произойти при возникновении исключения, см. Замки и исключения не смешиваются .

31 голосов
/ 18 мая 2011

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

Предпочтительным механизмом является (и обратите внимание на ref):

bool lockTaken = false;
try {
    Monitor.TryEnter(lockObj, timeout, ref lockTaken);
    if(!lockTaken) throw new TimeoutException(); // or compensate
    // work here...
} finally {
    if(lockTaken) Monitor.Exit(lockObj);
}

Это позволяет избежать риска снятия блокировки в некоторых крайних случаях.

finally (который существует в любой разумной реализации) обеспечивает снятие блокировки даже в условиях ошибки.

9 голосов
/ 18 мая 2011

Простой lock(syncRoot) будет ждать вечно.

Вы можете заменить его на

if (System.Threading.Monitor.TryEnter(syncRoot, 1000))
{
     try
     {
         DoIt();
     }
     finally
     {
         System.Threading.Monitor.Exit(syncRoot);
     }
}

Каждая нить должна обеспечивать исключительную блокировку.

Обратите внимание, что стандарт lock(syncRoot) {} переписан на Monitor.Enter(syncRoot) и попытка / наконец

3 голосов
/ 18 мая 2011

Замок будет ждать вечно, как сказал Хенк.Исключение все равно будет разблокировано.Он реализован внутри с помощью блока try-finally.

1 голос
/ 18 мая 2011

Вы должны сделать шаг назад и спросить себя, почему вы позволяете исключению из метода иметь какое-либо мнение, когда Monitor you Enter равно Exit ed.Если даже существует вероятность того, что DoIt() может вызвать исключение (и я бы сказал, что, если возможно, вы переписываете DoIt(), чтобы он не делал), тогда у вас должен быть блок try/catch внутри оператора lock ()что вы можете быть уверены, что выполните любую необходимую очистку.

0 голосов
/ 21 апреля 2017

Знайте необходимые предварительные условия для возникновения тупика. Всегда используйте Monitor vs Lock, чтобы избежать взаимоблокировок.

Conditions

0 голосов
/ 20 марта 2015

Jason anwser превосходен, вот способ обернуть его, упростив вызов.

    private void LockWithTimeout(object p_oLock, int p_iTimeout, Action p_aAction)
    {
        Exception eLockException = null;
        bool bLockWasTaken = false;
        try
        {
            Monitor.TryEnter(p_oLock, p_iTimeout, ref bLockWasTaken);
            if (bLockWasTaken)
                p_aAction();
            else
                throw new Exception("Timeout exceeded, unable to lock.");
        }
        catch (Exception ex)
        {
            // conserver l'exception
            eLockException = ex;
        }
        finally 
        { 
            // release le lock
            if (bLockWasTaken) 
                Monitor.Exit(p_oLock); 

            // relancer l'exception
            if (eLockException != null)
                throw new Exception("An exception occured during the lock proces.", eLockException);
        }
    }

, а затем используйте его следующим образом:

        // ajouter à la liste des fiches à loader
        LockWithTimeout(m_lLoadingQueue, 3600, () =>
        {
            m_lLoadingQueue.Add(p_efEcranFiche);
        });
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...