如何基于密钥同步代码块
本文关键字:代码 同步 密钥 何基于 | 更新日期: 2023-09-27 17:57:41
我有一个通过WCF MSMQ传输公开的服务。
该服务所做的部分工作是基于关键字(source,item_id)查找项。如果它找到一个,它将检索数据库标识符,并使用该标识符来更新记录。如果找不到,则插入新记录。
我注意到两个项目可能同时进入,两个项目都看到数据库中不存在一个项目,他们都试图插入,但其中一个项目因约束错误而失败。
我想限制对数据库查找的访问,并根据键(source,item_id)跟踪代码,以便一次只有一个线程可以为该特定键执行工作。
我已经编写了一些代码来实现这一点,但我想得到一些反馈,看看这是否有效,或者是否有更好的方法。
使用LockManager
类的代码:
public class ItemService
{
private static LockManager lockManager = new LockManager();
public void AddItem(Item item){
var itemKey = item.Source + ":" + item.ItemId;
lockManager.Work(itemKey, delegate(){ do stuff });
}
}
LockManager
类:
public class LockManager
{
private readonly IDictionary<string, LockObject> _lockTable =
new Dictionary<string, LockObject>();
public void Work(string key, Action work)
{
var lockObject = BorrowLockObject(key);
try
{
lock (lockObject)
{
work();
}
}
finally
{
ReturnLockObject(lockObject);
}
}
private LockObject BorrowLockObject(string key)
{
lock (_lockTable)
{
LockObject lockObject = null;
if (_lockTable.ContainsKey(key))
{
lockObject = _lockTable[key];
}
else
{
lockObject = new LockObject(key);
_lockTable[key] = lockObject;
}
lockObject.Open();
return lockObject;
}
}
private void ReturnLockObject(LockObject lockObject)
{
lock (_lockTable)
{
if (lockObject.Close())
{
_lockTable.Remove(lockObject.GetKey());
}
}
}
}
LockObject
类:
public class LockObject
{
private readonly string _key;
private int _count;
public LockObject(string key)
{
_key = key;
_count = 0;
}
public string GetKey()
{
return _key;
}
public void Open()
{
lock(this)
{
_count++;
}
}
/// <summary>
/// Closes this lock object.
/// </summary>
/// <returns>True if this Lock Object is no longer in use.</returns>
public bool Close()
{
lock(this)
{
_count--;
return _count == 0;
}
}
}
Pair<Key,Action>
和WorkManager
的同步集合在一个单独的线程上作为队列(从后到前)工作将大大简化这一过程。您可以弹出并丢弃所有包含相同密钥的对,然后弹出一个进行处理并完成处理(在执行此操作时锁定集合)。
>
- 客户端添加
- 锁集合
- 添加
- 解锁集合
>
- 后台线程迭代:
- 锁集合
- 获取工作项(集合中的最后一个)
- 从集合中删除
- 解锁集合
- 工作。。。(与此同时,客户端会添加更多,甚至可能重复)
- 锁集合
- 用同一把钥匙取出所有物品(并妥善处理)
- 解锁集合
BTW:public delegate void Worker();
在Action
中有一个快捷方式。
这很有效。2件事:字典永远不会发布关键字和值;如果您想同时获取两个锁,请确保通过始终按相同顺序访问它们(对密钥进行排序)来避免死锁。