How long will a C# lock wait, and what if the code

2020-02-16 07:33发布

I saw the following code, and wanted to use it for a simple activity which may only be executed one at a time, and won't occur frequently (so the chance of occurring twice at a time is very small, but you never know).

So the code:

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

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

When another thread comes by and wants to execute the code, how long will it wait until the lock is released? Forever, or can you somehow set a timeout?

And second: if the DoIt() method throws an exception, is the lock still released?

标签: c# locking
7条回答
你好瞎i
2楼-- · 2020-02-16 07:57

Know the necessary preconditions for deadlock to take place. Always use Monitor vs Lock to avoid deadlocks.

Conditions

查看更多
疯言疯语
3楼-- · 2020-02-16 08:00

You should take a step back and ask yourself why you are letting an exception from a method have any say over when a Monitor you Enter is Exited. If there's even a chance that DoIt() might throw an exception (and I would argue that if possible you re-write DoIt() so that it DoesNot), then you should have a try/catch block within the lock() statement so that you can ensure you perform any necessary clean-up.

查看更多
▲ chillily
4楼-- · 2020-02-16 08:00

Jason anwser is excellent, here is a way to wrap it making the call simpler.

    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);
        }
    }

and then, use it this way:

        // ajouter à la liste des fiches à loader
        LockWithTimeout(m_lLoadingQueue, 3600, () =>
        {
            m_lLoadingQueue.Add(p_efEcranFiche);
        });
查看更多
兄弟一词,经得起流年.
5楼-- · 2020-02-16 08:02

The lock will wait forever, as Henk said. An exception will still unlock. It is implemented internally with a try-finally block.

查看更多
该账号已被封号
6楼-- · 2020-02-16 08:09

As mentioned, a regular lock will wait forever, which is a risk of deadlocks.

The preferred mechanism is (and note the 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);
}

This avoids the risk of not releasing the lock in some edge-cases.

The finally (which exists in any sensible implementation) ensures the lock is released even in error conditions.

查看更多
爷、活的狠高调
7楼-- · 2020-02-16 08:10

A simple lock(syncRoot) will wait forever.

You can replace it with

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

Every thread should ensure exception-safe locking.

Note that the standard lock(syncRoot) {} is rewritten to Monitor.Enter(syncRoot) and a try/finally

查看更多
登录 后发表回答