【问题标题】:lock statement does not appear to be workinglock 语句似乎不起作用
【发布时间】:2011-12-25 21:59:45
【问题描述】:

我有这个方法:

public bool Remove(EntityKeyType key)
{
    lock (syncroot)
    {
        //wait if we need to
        waitForContextMRE.Wait();

        //if the item is not local, assume it is not remote.
        if (!localCache.ContainsKey(key)) return false;

        //build an expression tree
        Expression<Func<EntityType, bool>> keyComparitorExpression = GenerateKeyComparitorExpression(key);

        var itemToDelete = TableProperty.Single(keyComparitorExpression);

        //delete from db
        TableProperty.DeleteOnSubmit(itemToDelete);
        DataContext.SubmitChanges();

        //get the removed item for OnCollectionChanged
        EntityType itemToRemove = localCache[key];
        itemToRemove.PropertyChanged -= item_PropertyChanged;

        //remove from the list
        Debug.Assert(localCache.Remove(key));

        //call the notification
        OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, itemToRemove));
        return true;
    }
}

我从多个线程调用它(调用同一个实例),但 TableProperty.Single 上不断抛出异常(序列不包含任何元素)。在调试代码时,我看到正在创建一种情况,即在另一个线程检查缓存是否存在之后,从数据库中删除该项目。这应该是不可能的,除非在 lock 语句中有多个线程(syncroot 对象肯定是跨线程的同一个实例)。

不可能?我有证据:

lock 语句里面有三个线程!什么给了?

注释:

  1. MRE 已设置(未阻塞)。
  2. 这不是引发异常的情况,它只是显示锁定部分内的多个线程。 更新:我将图像更改为异常的智能跟踪事件。老图是here
  3. syncroot 对象不是静态的,因为我只想同步对同一实例的调用。

更新

这是同步根对象的声明:

private object syncroot = new object();

还有其他一些声明:

private ManualResetEventSlim waitForContextMRE = new ManualResetEventSlim(true);
private DataContextType _dataContext;
private System.Data.Linq.Table<EntityType> _tableProperty;
//DataContextType and EntityType are generic type parameters

我无法将同步根设为静态,因为我有多个类实例正在运行,重要的是它们不会相互阻塞。但这并不重要 - 将其设为静态并不能解决问题。

ManualResetEvent (waitForContextMRE) 不用于同步 - 它用于在执行某些操作后(即启动时)阻塞数据库操作一段时间。大部分时间都是这样设置的。将其从锁块中取出也不能解决问题。

【问题讨论】:

  • A:我们能看到你在哪里初始化syncroot,B:对象上下文会持续多久? C:你确定它们是同一个实例吗?很难从暂停状态中分辨出来......
  • 我建议使用 System.Diagnostics.Debug.WriteLine 添加跟踪消息。在锁块的开头和结尾写上线程id和syncroot hashcode。然后你可以很容易地在输出窗口中看到两个线程是否与同一个同步根重叠,这绝对不应该发生。
  • 将syncroot设为静态有什么危害?
  • @Bob 大概是为了比每个应用一个更细化;在许多应用程序中,非常需要避免静态锁
  • @Bob 绝对;假设这是一个使用率很高的区域,它会使您的多核服务器相当无用 - 而是只允许一个调用者。如果数据(看起来,OP 打算)是分开的,则它们无需相互阻止。如果它只是本地访问,那将是不可取的,但可以原谅(至少在分析表明它很重要之前)-但是当将网络访问(在这种情况下为数据库)添加到组合中时,它可以代表一个重要 夹点。

标签: c# multithreading locking


【解决方案1】:

我唯一的解释是 waitForContextMRE.Wait();调用它会使线程解除阻塞同步根!所以其他线程可以进入锁定部分。尝试移动 waitForContextMRE.Wait();在锁之前(...)。

【讨论】:

  • 我认为我们对 waitForContextMRE 的了解还不够多,因此无法得出任何结论; Monitor.Wait(syncroot) 是唯一会释放锁的东西——对我来说这并不明显
  • 如果您在锁内并假设没有其他进程使用该线程,为什么要调用 wait() ? //如果我们需要waitForContextMRE.Wait();
【解决方案2】:

我认为您正在调用不同的对象。您的屏幕截图上没有任何迹象表明您正在从不同的线程中获取值。 也使用非静态同步根不是一个好主意,因为可能会导致像你这样的情况。你真的有充分的理由不让它静态吗?

【讨论】:

  • @Elastep 我同意作为同一个对象的证据相当缺乏 - 因此我要求提供更多信息作为(未答复的)评论......
  • 它必须是非静态的,因为对象的多个实例不能相互阻塞。这段代码是从单元测试中调用的,在这种情况下只有一个实例。
【解决方案3】:

我建议锁定 TableProperty 或 DataContext

【讨论】:

  • 出于好奇...基于?线程问题非常复杂,应该避免任何下意识的反应,任何建议都应该是:a:基于证据,b:包括推理
  • 我无法锁定 TableProperty 或 DataContext,因为在某些情况下它们被更改(处置和重新创建),在这种情况下它也会锁定同步根对象。
【解决方案4】:

我已经调试这个问题一段时间了,虽然我还没有解决它,但我很清楚锁正在工作。我猜问题出在 DataContext 上(众所周知,它在多线程情况下很棘手)。

【讨论】:

  • 我发现了问题 - Debug.Assert(localCache.Remove(key)) - 就像在 C++ 中一样,断言中的内容不会在发布模式下运行,导致项目从数据库中删除,而不是从缓存中删除。跨度>
猜你喜欢
  • 1970-01-01
  • 2018-10-03
  • 1970-01-01
  • 2020-06-23
  • 1970-01-01
  • 2013-07-23
  • 2017-10-13
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多