【问题标题】:Question on using Monitor.TryEnter and locking object关于使用 Monitor.TryEnter 和锁定对象的问题
【发布时间】:2010-08-25 09:09:27
【问题描述】:

考虑以下实现仅对一个线程的非阻塞访问的函数。

public bool TryCancelGroup()
{
    if (Monitor.TryEnter(_locked))
    {
        if (_locked == false)
        {
            _locked = true;

            try
            {
                // do something
            }
            catch (Exception ex)
            {
                _locked = false;
            }
            finally
            {
                Monitor.Exit(_locked);
            }
        }
        return _locked;
    }
    else
    {
        return false;
    }
}

这是_locked 变量的定义方式。

bool _locked = false;

现在当程序到达Monitor.Exit(_locked); 时,它会抛出一个System.Threading.SynchronizationLockException,表示_locked 变量之前没有同步。

在 _locked 变量被定义为对象之前,这一切都在工作

object _locked = new object();

当我将其更改为 bool 以将其用作布尔标志时,我开始收到此异常。

【问题讨论】:

  • JaredPar 为您的问题提供了答案。但是您的逻辑似乎有缺陷-如果您使用 _locked = true 进入监视器,那么您将永远不会退出监视器。
  • 哦当然可以,谢谢VinayC
  • 另外我认为你通常有一个静态对象作为你的锁。
  • @Chris。不是我的情况。我需要为类实例而不是类类型提供同步。因此我不需要静态锁定对象。
  • 酷。我确实认为可能是这种情况,但想检查它不仅仅是一个错字或什么的。 :)

标签: c# .net multithreading locking


【解决方案1】:

原因是Monitor 方法都带有System.Object 参数。当您传入bool 时,需要一个框来转换为Object。 box 操作为每个调用生成一个新的System.Object 值。所以TryEnterExit 方法看到不同的对象并导致异常。

_locked 输入到Object 时,不需要一个框。因此TryEnterExit 方法看到相同的对象并且可以正常工作。

其他一些关于代码的cmets

  • TryEnter 在所有情况下都必须与 Exit 配对,并且出于理智考虑,Exit 调用应该在 finally 块中。否则,您将引发死锁场景
  • _locked 变量仅在遇到异常时才设置为false。如果执行没有产生异常,它将保持为真,并且没有线程将再次进入if 块。

【讨论】:

  • 哦是的拳击。但是布尔类型不是从 System.Object 派生的吗?所以它是从 Object 派生的,但是当在某个地方遇到到 Object 的转换时,是否使用了装箱操作?这个说法正确吗?
  • @Captain,当任何类型的值类型,包括bool,用于键入System.Object的位置或接口时,将发生装箱操作。
【解决方案2】:

将监视器上的超时设置为 0 有助于实现您想要的行为。使用全局声明的对象进行锁定。

static object mylock = new object();

....

if (Monitor.TryEnter(mylock, 0))
{
    try
    {
           // Do work
    }
    finally
    {
        Monitor.Exit(mylock);
    }
}

【讨论】:

  • Monitor.TryEnter(mylock, 0) 与 Monitor.TryEnter(mylock) 就我在反射器中看到的一样。
  • 不是根据我的经验。我将“,0”添加到我的,它解决了我遇到的问题。
  • @Andrew static object var mylock 是无效的变量声明。
  • @DerreckDean - 令人惊讶的是添加 , 0 修复了一个错误; monitor spec 说0等于省略参数:“如果millisecondsTimeout等于0,这个方法等价于TryEnter(Object)。
  • @tchelidze - 我同意,所以我已经编辑以修复声明。
【解决方案3】:

为了增加上面的成功 - 确保锁被释放 - TryEnter() 和 Exit() 可以包装在自定义类中,作为 object 的扩展,采用 Delegate 和 Timeout作为参数。

public static class MyMonitor
{

    public static bool TryEnter(this object obj, Action action, int millisecondsTimeout)
    {
            if (Monitor.TryEnter(obj, millisecondsTimeout))
            {
                try
                {
                    action();
                }
                finally
                {
                    Monitor.Exit(obj);
                }
                return true;
            }
            else
            {
                return false;
            }
    }
}

并像这样调用,等待 1000 毫秒以获取锁,如果超时则抛出错误:

if (!_locked.TryEnter(() =>
{
    //Exclusive access code placed here..
}, 1000)) {
    throw new TimeoutException("Timeout waiting for exclusive access");
}

这样忘记 Monitor.Exit() 不是一种选择。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-01
    • 2011-05-14
    相关资源
    最近更新 更多