如何基于密钥同步代码块

本文关键字:代码 同步 密钥 何基于 | 更新日期: 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件事:字典永远不会发布关键字和值;如果您想同时获取两个锁,请确保通过始终按相同顺序访问它们(对密钥进行排序)来避免死锁。