【问题标题】:Is there a "try to lock, skip if timed out" operation in C#?C# 中是否有“尝试锁定,如果超时则跳过”操作?
【发布时间】:2008-08-12 06:19:38
【问题描述】:

我需要尝试锁定一个对象,如果它已经锁定就继续(在超时之后,或者没有它)。

C# lock 语句被阻塞。

【问题讨论】:

    标签: c# multithreading locking


    【解决方案1】:

    Ed 为您提供了正确的功能。只是不要忘记致电Monitor.Exit()。您应该使用try-finally 块来保证正确清理。

    if (Monitor.TryEnter(someObject))
    {
        try
        {
            // use object
        }
        finally
        {
            Monitor.Exit(someObject);
        }
    }
    

    【讨论】:

    • +1,谢谢!如果您愿意,建议进行一些更改,以提醒它(几乎)总是可以使用高级构造/模式而无需无限期地嵌套大括号。
    • @ceztko,你能澄清一下吗?通常,在例如 C# 中释放 N 个资源需要 N 个嵌套的 try-finally 块。 using 语句仅对 IDisposables 有助于避免这种情况。如果发生异常,跳过 try-finally 块可能会导致资源泄漏。例如,Monitor 文档阐明 finally 块是必要的。奇怪的是,Ed 的链接忽略了这一点。
    【解决方案2】:

    我相信你可以使用Monitor.TryEnter()

    lock 语句只转换为Monitor.Enter() 调用和try catch 块。

    【讨论】:

    【解决方案3】:

    我遇到了同样的问题,我最终创建了一个实现 IDisposable 的类 TryLock,然后使用 using 语句来控制锁的范围:

    public class TryLock : IDisposable
    {
        private object locked;
    
        public bool HasLock { get; private set; }
    
        public TryLock(object obj)
        {
            if (Monitor.TryEnter(obj))
            {
                HasLock = true;
                locked = obj;
            }
        }
    
        public void Dispose()
        {
            if (HasLock)
            {
                Monitor.Exit(locked);
                locked = null;
                HasLock = false;
            }
        }
    }
    

    然后使用下面的语法来加锁:

    var obj = new object();
    
    using (var tryLock = new TryLock(obj))
    {
        if (tryLock.HasLock)
        {
            Console.WriteLine("Lock acquired..");
        }
    }
    

    【讨论】:

    • 由于 obj 不是静态的,因此代码每次运行时都会获得锁。假设它在课堂上,没有什么能阻止它获得锁。相反,如果您继承了 TryLock 并且继承的锁具有自己的静态锁对象,那么您可以在任何地方依赖该锁定。
    • @Tod 上面第二个代码 sn-p 中的obj 实例只是一个如何使用 TryLock 类锁定任何对象的示例。显然,没有必要在线程之间以某种方式共享的对象(即静态实例或单例)进行锁定 - 所以你是对的,我们通常不会初始化局部变量对象实例并锁定它。
    【解决方案4】:

    考虑使用带有超时输入的 AutoResetEvent 及其方法 WaitOne。

    static AutoResetEvent autoEvent = new AutoResetEvent(true);
    if(autoEvent.WaitOne(0))
    {
        //start critical section
        Console.WriteLine("no other thread here, do your job");
        Thread.Sleep(5000);
        //end critical section
        autoEvent.Set();
    }
    else
    {
        Console.WriteLine("A thread working already at this time.");
    }
    

    https://msdn.microsoft.com/en-us/library/cc189907(v=vs.110).aspx https://msdn.microsoft.com/en-us/library/system.threading.autoresetevent(v=vs.110).aspxhttps://msdn.microsoft.com/en-us/library/cc190477(v=vs.110).aspx

    【讨论】:

    • 如果 millisecondsTimeout 为零,则该方法不会阻塞。它测试等待句柄的状态并立即返回。这个对我有用,可能是最好的答案。我正在编辑以提供现成的解决方案。
    【解决方案5】:

    既然其他人已经为您指出了正确的方向,您可能会自己发现这一点,但 TryEnter 也可以采用超时参数。

    Jeff Richter 的“CLR Via C#”是一本关于 CLR 内部细节的优秀书籍,如果您正在研究更复杂的东西。

    【讨论】:

      猜你喜欢
      • 2011-12-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-11-14
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多