使用哈希表来锁定单个数据库行

时间:2012-03-16 10:48:27

标签: .net locking blocking

我正在尝试处理非标准数据库的锁(即它本身不提供此功能)。我的程序具有对db的独占访问权限,但是有多个并发线程需要同步。

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

要实现此功能,我的想法是使用一个公共哈希表来存储当前正在使用的所有行ID。需要同步访问此哈希表,通常是通过锁定它。

但是,如果我们发现我们要使用的行已经在使用,我们必须等待它被释放。这并非完全无足轻重,我的猜测是使用信号等待。

但是,我不确定如何做到这一点。你能想到一个实现这个功能的好方法吗?

2 个答案:

答案 0 :(得分:0)

您想要每行使用哈希表项是好的。作为此哈希表中的值,我将为此特定行存储一个锁。任何想要访问行的线程都会先检索(或创建)此哈希表中的相应锁。然后它将锁定并稍后释放它。

重要的是,对全局哈希表的所有访问都非常快。每行锁定一个对象可以解决这个问题,因为一旦您检索到锁定对象,无论您的操作持续多长时间,都可以立即解锁哈希表。

答案 1 :(得分: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