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

时间:2011-05-26 18:22:59

标签: c# .net 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 });
   }
}

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;
        }
    }
}

2 个答案:

答案 0 :(得分:1)

Pair<Key,Action>WorkManager的同步集合在一个单独的线程上工作,作为一个队列(从前到后)将大大简化这一过程。你可以弹出并丢弃所有包含相同密钥的Pairs,然后弹出一个工作并完成工作(在执行此操作时锁定集合)。

&GT;

  • 客户补充道
    • 锁定集合
    • 添加
    • 解锁收藏

&GT;

  • 反向线程迭代:
    • 锁定集合
    • 获取工作项目(最后收集)
    • 从收藏中删除
    • 解锁收藏
    • 工作......(同时客户增加更多,也许是重复)
    • 锁定集合
    • 删除所有具有相同密钥的项目(并正确处理)
    • 解锁收藏

BTW:public delegate void Worker();Action中有一个快捷方式。

答案 1 :(得分:0)

这很有效。 2件事:字典永远不会释放键和值;如果你想同时获得两个锁,请务必通过以相同的顺序访问它们来避免死锁(对键进行排序)。