【发布时间】: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 语句里面有三个线程!什么给了?
注释:
- MRE 已设置(未阻塞)。
- 这不是引发异常的情况,它只是显示锁定部分内的多个线程。 更新:我将图像更改为异常的智能跟踪事件。老图是here
- 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