Использование хеш-таблицы для блокировки отдельных строк базы данных - PullRequest
0 голосов
/ 16 марта 2012

Я пытаюсь обработать блокировки для нестандартной базы данных (то есть она сама не обеспечивает эту функцию).Моя программа имеет монопольный доступ к БД, но есть несколько параллельных потоков, которые необходимо синхронизировать.

Наивная реализация, которую я использовал ранее, заключалась в глобальной блокировке, не позволяющей параллельный доступ к отдельным строкам (что предусмотреновсегда потокобезопасен).

Для реализации этой функции я бы хотел использовать общую хэш-таблицу, в которой хранятся все идентификаторы строк, которые используются в данный момент.Доступ к этой хеш-таблице должен быть синхронизирован, обычно с помощью блокировки на нем.

Однако, если мы обнаружим, что строка, которую мы хотим использовать, уже используется, мы должны дождаться ее освобождения.Это не совсем тривиально, я предполагаю использовать сигнал для ожидания.

Хотя я не уверен, как именно это сделать.Можете ли вы придумать хороший способ для достижения этой функциональности?

Ответы [ 2 ]

0 голосов
/ 16 марта 2012

Я придумал эту наивную реализацию.

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 голосов
/ 16 марта 2012

Ваша идея использовать элемент хеш-таблицы в строке - это хорошо. В качестве значения в этой хеш-таблице я бы сохранил блокировку для этой конкретной строки. Любой поток, желающий получить доступ к строке, сначала извлечет (или создаст) соответствующую блокировку в этой хеш-таблице. Затем он захватит блокировку, а затем освободит ее.

Важно, чтобы весь доступ к глобальной хеш-таблице был очень быстрым. Наличие объекта блокировки в строке соответствует этой цели, потому что после получения объекта блокировки вы можете сразу разблокировать хэш-таблицу независимо от того, как долго будет длиться ваша операция.

...