如何基于密钥同步代码块?

ScA*_*er2 6 .net c# wcf thread-safety

我有通过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 });
   }
}
Run Code Online (Sandbox Code Playgroud)

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());
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

usr*_*usr 0

这有效。两件事:字典永远不会释放键和值;如果您想同时获取两个锁,请确保始终以相同的顺序访问它们(对键进行排序)来避免死锁。