2012-03-16 35 views
0

我试图处理非标准数据库的锁(即它本身不提供此功能)。我的程序可以独占访问数据库,但有多个并发线程需要同步。使用散列表锁定各个数据库行

我之前使用的天真实现是全局锁定,不允许并行访问单独行(这是由设计总是线程安全的)。

为了实现这个功能,我的想法是使用一个通用的散列表,它存储了现在正在使用的所有行标识符。访问这个哈希表需要同步,通常通过锁定它。

但是,如果我们发现我们要使用的行已被使用,我们必须等待它被释放。这不是微不足道的,我的猜测是使用信号等待。

虽然我不确定究竟该怎么做。你能想出一个实现这个功能的好方法吗?

回答

0

你对每行使用散列表项的想法很好。作为这个散列表中的值,我会为这个特定行存储一个锁。任何希望访问行的线程都会首先检索(或创建)该哈希表中相应的锁。然后它会锁定,然后释放它。

重要的是,对全局散列表的访问非常快速。每行有一个锁对象可以解决该目标,因为一旦检索到锁对象,无论操作持续多久,您都可以立即解锁散列表。

0

我想出了这个天真的实现。

internal class LockingTest 
{ 
    private readonly Dictionary<string, ReaderWriterLockSlim> _Locks = new Dictionary<string, ReaderWriterLockSlim>(); 

    private void LockRow (string id) 
    { 
     ReaderWriterLockSlim slim; 

     for (; ;) { 
      lock (_Locks) { 
       /* 
       * if row not in use, grab it 
       */ 
       if (!_Locks.TryGetValue (id, out slim)) { 
        slim = new ReaderWriterLockSlim(); // this can probably be replaced by a different, cheap signal class 
        slim.EnterWriteLock(); 
        _Locks.Add (id, slim); 
        return; 
       } 
      } 

      /* 
      * row is in use, wait until released, then try again 
      */ 
      slim.EnterWriteLock(); 
     } 
    } 

    private void UnlockRow (string id) 
    { 
     /* 
     * release and remove lock 
     */ 
     lock (_Locks) { 
      var slim = _Locks[id]; 
      _Locks.Remove (id); 
      slim.ExitWriteLock(); 
     } 
    } 

    public void Test() 
    { 
     var rnd = new Random(); 

     Action thread =() => { 
      for (; ;) { 
       var id = rnd.NextDouble() < 0.5 ? "a" : "b"; 

       Console.WriteLine (Thread.CurrentThread.Name + " waits for " + id); 
       LockRow (id); 
       Console.WriteLine (Thread.CurrentThread.Name + " locked " + id); 
       Thread.Sleep (rnd.Next (0, 100)); 

       UnlockRow (id); 
       Console.WriteLine (Thread.CurrentThread.Name + " released " + id); 
       Thread.Sleep (rnd.Next (0, 100)); 
      } 
     }; 

     new Thread (() => thread()) { 
      Name = "L1", 
      IsBackground = true 
     }.Start(); 

     new Thread (() => thread()) { 
      Name = "L2", 
      IsBackground = true 
     }.Start(); 

     Thread.Sleep (1000); 
    } 

输出

 
L1 waits for a 
L1 locked a 
L2 waits for b 
L2 locked b 
L2 released b 
L1 released a 
L1 waits for a 
L1 locked a 
L2 waits for a 
L1 released a 
L2 locked a 
L2 released a 
L1 waits for b 
L1 locked b 
L2 waits for b 
L1 released b 
L2 locked b 
L1 waits for a 
L1 locked a 
L2 released b 
L1 released a 
L1 waits for a 
L1 locked a 
L2 waits for a 
L1 released a 
L2 locked a 
L1 waits for b 
L1 locked b 
L1 released b 
L2 released a 
L1 waits for b 
L1 locked b 
L1 released b 
L2 waits for a 
L2 locked a 
L2 released a 
L1 waits for b 
L1 locked b 
L2 waits for a 
L2 locked a 
L2 released a 
L1 released b 
L1 waits for b 
L1 locked b 
L1 released b 
L2 waits for a 
L2 locked a 
L1 waits for a 
L2 released a 
L1 locked a 
L2 waits for b 
L2 locked b 
L1 released a 
L1 waits for b 
L2 released b 
L1 locked b 
L1 released b 
+0

更新的源代码,包括除了本地锁全局锁看到https://github.com/mafutrct/Zysl/blob/master/trunk/Zysl/Utils/BlockingSet。 CS – mafu 2012-03-16 15:10:30