【问题标题】:Test a lock with out acquiring it?在不获取锁的情况下测试锁?
【发布时间】:2010-02-18 23:13:46
【问题描述】:

我有对象,它们有锁。我想测试它们是否在没有获得锁的情况下被锁定。这个想法是如果我TryEnter() 然后我必须Exit() 如果true 只检查正确的锁。

这似乎是一个非常基本的问题,它是如何完成的?

【问题讨论】:

    标签: c# multithreading locking exit


    【解决方案1】:

    当你看到锁解锁后,你能得到什么信息?当您根据该信息做出决定时,锁定可能已经被占用。

    【讨论】:

    • +1,完全正确。回答这个关于锁的过去状态的问题只会导致你做出错误的决定。
    • 当您检查 IsBeingUsed 时,线程可能已经停止了有用的工作,但可能在 IsBeingUsed 更新之前发生了上下文切换。
    • @Eric: 或更糟:IsBeingUsed 为 0,而您继续进行更改,却没有注意到在此期间有人确实将其更改为 1。
    • 我可以看到这种行为的用途。想象一下长时间获取锁的情况,两个线程需要对同一组对象做不同的事情。如果这两个收集操作不依赖于顺序,那么首先查看一个对象可能很有用,如果它正在使用中,则跳过它以在稍后的通道中处理。
    • @Tullo:但这可以通过像 Monitor.TryEnter 这样的零等待获取更好地解决。零等待获取意味着您愿意在空闲时抢锁,但不愿意等待。 OP要求不同的语义,即。 peek 无需获取。我真的看不出这种语义有什么实际用途。
    【解决方案2】:

    因为lock语句等价于:

    System.Threading.Monitor.Enter(x);
    try {
       ...
    }
    finally {
       System.Threading.Monitor.Exit(x);
    }
    

    你能做到吗?

    bool ObjectWasUnlocked(object x)
    {
       if(System.Threading.Monitor.TryEnter(x))
       {
           System.Threading.Monitor.Exit(x);
           return true;
       }
       else
       {
           return false;
       }
    }
    

    请注意,我将此函数命名为“ObjectWasUnlocked”,而不是“ObjectIsUnlocked”。不能保证当函数返回时它仍然会被解锁。

    【讨论】:

    • 在这个解决方案中,虽然你获得了 OP 不想做的锁。
    【解决方案3】:

    在尝试审核我的代码以确保正确锁定时,我也想知道同样的事情。我想出了一个使用第二个线程的方法。如果锁对调用线程可用,但对第二个线程不可用,则它必须由第一个线程持有。

        /// <summary>
    /// Utiltity for checking if a lock has already been acquired.
    /// WARNING: This test isn't actually thread-safe, 
    /// it's only really useful for unit tests
    /// </summary>
    private static bool ObjectIsAlreadyLockedByThisThread(object lockObject)
    {
        if (!Monitor.TryEnter(lockObject))
        {
            // another thread has the lock
            return false;
        }
    
        Monitor.Exit(lockObject);
    
        bool? LockAvailable = null;
    
        var T = new Thread(() =>
        {
            if (Monitor.TryEnter(lockObject))
            {
                LockAvailable = true;
                Monitor.Exit(lockObject);
            }
            else
            {
                LockAvailable = false;
            }
        });
    
        T.Start();
    
        T.Join();
    
        return !LockAvailable.Value;
    }
    
    // Tests:
    public static void TestLockedByThisThread()
    {
        object MyLock = new object();
        lock (MyLock)
        {
            bool WasLocked = ObjectIsAlreadyLockedByThisThread(MyLock);
    
            Debug.WriteLine(WasLocked); // prints "True"
        }
    }
    
    public static void TestLockedByOtherThread()
    {
        object MyLock = new object();
    
        var T = new Thread(() =>
        {
            lock (MyLock)
            {
                Thread.Sleep(TimeSpan.FromSeconds(2));
            }
        });
    
        T.Start();
        Thread.Sleep(TimeSpan.FromSeconds(1));
    
        bool WasLocked = ObjectIsAlreadyLockedByThisThread(MyLock);
    
        T.Join();
    
        Debug.WriteLine(WasLocked); // prints "False"
    }
    
    public static void TestNotLocked()
    {
        object MyLock = new object();
    
        bool WasLocked = ObjectIsAlreadyLockedByThisThread(MyLock);
    
        Debug.WriteLine(WasLocked); // prints "False"
    }
    

    我不会在生产代码中使用它 - 存在可能会爆发的竞争条件。但是,我的单元测试大多是单线程的,所以这很有用。

    【讨论】:

    • 此代码已损坏。即使无法获取锁,也会调用第一个 Monitor.Exit(lockObject)(在 finally 块中)。
    • @Sebastian Krysmanski:好点,不需要finally。修复并清理
    【解决方案4】:

    这是一个相关的问题

    Checking whether the current thread owns a lock

    结论是'你不能'

    【讨论】:

    • 自 4.5 起不再存在
    猜你喜欢
    • 2013-10-28
    • 1970-01-01
    • 2013-11-15
    • 1970-01-01
    • 1970-01-01
    • 2010-09-11
    • 1970-01-01
    • 2011-03-27
    相关资源
    最近更新 更多