Блокировка по тайм-ауту

lock использует этот шаблон

if(Monitor.Enter(lock))
    try
    {
        ...
    }
    finally { Monitor.Exit(lock); } // using this style to reduce post "height"

если мы не хотим ждать бесконечно, мы можем предоставить тайм-аут

if(!Monitor.TryEnter(lock, timeout))
    throw new TimeoutException();
try
{
    ...
}
finally { Monitor.Exit(lock); }

У меня есть сценарий, когда метод должен получить несколько блокировок, прежде чем начать что-либо делать. Это выглядит ужасно:

if(!Monitor.TryEnter(lockA, timeout))
    throw new TimeoutException();
try
{
    if(!Monitor.TryEnter(lockB, timeout))
        throw new TimeoutException();
    try
    {
        if(!Monitor.TryEnter(lockC, timeout))
            throw new TimeoutException();
        try
        {
            ... // more of such constructions
        }
        finally { Monitor.Exit(lockC); }
    }
    finally { Monitor.Exit(lockB); }
}
finally { Monitor.Exit(lockA); }

У него есть проблемы:

выглядит некрасиво (код метода с отступом, представьте, как он будет выглядетьlockZ), можно решить, поместив код метода в другой метод.

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

Есть ли способ улучшить этот тайм-аут?

Я думал создать метод с параметром делегата и блокировкой для достижения чего-то вроде цепочки linq (но также и для параллельной работы блокировок это сложная задача):

Lock(lockA).Lock(lockB).Lock(lockC).Run( () => ...);

Или, может быть, есть другой способ?

Ответы на вопрос(1)

Я хотел бы использовать с синхронизацией блокировки:

using System;
using System.Threading; 

public class MyLock : IDisposable
{
    private object lockObj;

    public Lock(object lockObj, TimeSpan timeout)
    {
        this.lockObj = lockObj;
        if (!Monitor.TryEnter(this.lockObj, timeout))
            throw new TimeoutException();
    }

    public void Dispose()
    {
        Monitor.Exit(lockObj);
    }
}

Использование:

using(new MyLock(lockA, new TimeSpan.FromSeconds(1)))
using(new MyLock(lockB, new TimeSpan.FromSeconds(2)))
using(new MyLock(lockC, new TimeSpan.FromSeconds(3)))
{
    // your code
}

Не знаю, является ли "блокировка" в ctor хорошим шаблоном / дизайном, но это сработает;)

Для асинхронных. Паррализация не очень хорошая идея. Зачем? Если какой-либо поток войдет в монитор, тот же поток должен покинуть его (выход с блокировкой). Так что, если вы заблокируетеobjA вParallel.ForEach (например), вы не будете знать, какой поток это сделал. Так что вы не сможете его выпустить.

 Johan Boulé22 июн. 2016 г., 15:00
Блокировка в ctor определенно является распространенной моделью, особенно в C ++. Это называется RAII.

Ваш ответ на вопрос