2012-03-16 2 views
0

非標準データベースのロックを処理しようとしています(つまり、この機能自体は提供していません)。私のプログラムはdbに排他的にアクセスできますが、同期が必要な複数の同時スレッドがあります。ハッシュテーブルを使用して個々のデータベース行をロックする

これまで使用してきた純粋な実装は、グローバルにロックすることで、並列アクセスを別々の行に許可しないようにしました(これは常にスレッドセーフです)。

この機能を実装するには、私の考えは、現在使用されているすべての行IDを格納する共通のハッシュテーブルを使用することです。このハッシュテーブルへのアクセスは、通常はロックすることで同期する必要があります。

ただし、使用する行がすでに使用されていることがわかったら、その行が解放されるまで待つ必要があります。これは完全に自明ではない、私の推測は、信号を待つために使用することです。

どのように正確にこれを行うにはわかりません。この機能を実現する良い方法を考えることができますか?

答えて

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

関連する問題