2011-05-26 48 views
6

我有通过WCF MSMQ传输公开的服务。如何基于密钥同步一段代码?

此服务所做的一部分工作是查找基于某个键(source,item_id)的项目。 如果它找到一个,它将检索数据库标识并使用它来更新记录。 如果它没有找到,它会插入新记录。

我注意到两个项目可能同时进来,都看到一个项目不存在于数据库中,它们都尝试插入,但是一个失败并出现约束错误。

我想限制对数据库查找和基于密钥(源,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; 
     } 
    } 
} 
+0

我想我也可以通过我的dao调用使用事务来解决这个问题。我只需要使用适当的事务隔离。这可能是一个比上面的锁管理器更好的解决方案。 – ScArcher2 2011-05-31 16:25:59

回答

0

这工作。 2件事:字典永远不会释放键和值;如果您想要同时获取两个锁,请确保通过始终以相同顺序(按键排序)来访问它们以避免死锁。

+0

当没有其他线程使用密钥时,ReturnLockObject()方法应该从字典中删除密钥。对字典的访问也始终同步。 – ScArcher2 2011-05-26 19:29:49

1

在一个单独的线程上作为队列(回到前面)的Pair<Key,Action>WorkManager的同步集合将大大简化这一点。您可以弹出并丢弃所有包含相同密钥的对,然后弹出一个密钥对其进行工作并完成工作(在此过程中锁定收集)。

>

  • 客户端增加了
    • 锁征收
    • 添加
    • 解锁收集

>

  • backthread迭代:
    • 锁征收
    • 找到工作项目(在最后集合)
    • 从集合中删除
    • 解锁收集
    • 工作...(在此期间客户增加更多的,也许重复)
    • 锁征收
    • 删除与同一个密钥的所有项目(和妥善处理)
    • 解锁收集

BTW:public delegate void Worker();有快捷方式Action

+0

谢谢我用Action更新了我的代码。 – ScArcher2 2011-05-26 19:45:33

+0

这并不真正涵盖我的用例。我需要这是多线程的,我不能放弃项目更新。我宁愿管理锁定,也不愿意管理一组新线程来完成这项工作。 – ScArcher2 2011-05-26 21:00:21

+0

以及我很高兴它帮助至少与行动,我认为这是一个很好的选择关键字的最abtract数量的工作:) – 2011-05-26 23:41:40