Снимите блокировку с другого потока, чем владелец монитора - PullRequest
3 голосов
/ 03 февраля 2011

У меня есть критическая секция в коде, которая разделена двумя вызовами функций, скажем, Start() и End().Они используют Monitor для блокировки других потоков во время выполнения.Теперь моя проблема в том, что если какой-то поток по какой-либо причине не вызывает End(), весь мой процесс в беде, так как каждый поток ожидает освобождения этого Monitor.

Конечно, я мог бы использоватьTryEnter с таймаутом, так что я не буду ждать вечно, но это не освободит заблокированный Monitor, поэтому моя программа будет попадать в этот таймаут каждый раз с этого времени.

Есть лиспособ снять блокировку Monitor из другого потока, если заданный тайм-аут закончился?

void Start(){ Monitor.Enter(obj); }

void End(){ Monitor.Exit(obj); }

РЕДАКТИРОВАТЬ: Мы вызываем Excel через COM-взаимодействия, и мы не можем быть уверены, что Excelпроцесс всегда будет работать как положено.Обратите внимание, что это веб-приложение, поэтому отказ от рассмотрения этого дела смертелен.Start() вызывается в первый раз, запрос вызывает функцию Excel, End() вызывается в конце запроса.Всегда существует вероятность того, что процесс Excel начинает зависать.

EDIT2: Теперь у меня возникла идея сохранить владельца блокировки ent в переменной, и при тупиковой ситуации я мог бы убить этот поток.Разве это не сняло бы блокировку?

                        if (Monitor.TryEnter(excelLocker, 10000) == false)
                        {
                            excelOwner.Abort();
                            excelOwner = null;
                        }
                        else
                        {
                            excelOwner = Thread.CurrentThread;
                        }

Ответы [ 3 ]

2 голосов
/ 03 февраля 2011

Единственный поток, который может снять блокировку, - это поток, которому принадлежит блокировка. Так что нет, вы не можете напрямую «разблокировать» монитор из другого потока - это не возможно по замыслу. Если бы вы смогли сделать это, другие потоки смогли бы переопределить семантику блокировки, освободив ее, когда они фактически не владели ею.

Мне любопытно узнать, почему вы не используете блок lock, чтобы гарантировать Enter и Exit вместо непосредственного использования Monitor.

Обновление

После прочтения вашего комментария я настоятельно рекомендую организовать ваш код так, чтобы вы могли локализовать блокировку, а не по запросу начала и окончания запроса. Если вы используете lock, вы все равно можете сериализовать доступ к Excel, но вы можете гарантировать, что Enter и Exit вызваны.

К вашему сведению lock - это Monitor под капотом.

    lock(_syncObj)
    {
        //Do stuff
    }

    //Is equivalent to

    Monitor.Enter(_syncObj);
    try
    {
        //Do stuff
    }
    finally
    {
        Monitor.Exit(_syncObj);
    } 

Используя lock, вы можете локализовать блокировку Excel следующим образом:

    //Client code
    ExcelUtil.DoStuff("bling")

    //...

    //Util class manages call to Excel and locking.
    public static class ExcelUtil
    {
        private static readonly object SyncObj = new object();

        public static void DoStuff(string someParam)
        {
            //Guaranteed locking and unlocking even if an exception occurs
            lock (SyncObj)
            {
                DoSomeStuffWithExcelFuncA();
                DoSomeStuffWithExcelFuncB();
            }
        }

        private static void DoSomeStuffWithExcelFuncA()
        {
            //...
        }

        private static void DoSomeStuffWithExcelFuncB()
        {
            //...
        }
    }

Кстати, почему вы блокируете доступ к Excel? Я предполагаю, что вы используете серверную часть автоматизации Excel для своего приложения ASP.Net. Если что-то не изменилось, это всегда было очень хлопотно, по крайней мере, несколько лет назад. Если вы снимаете блокировку, а Excel зависает, вы набиты. Существуют сторонние решения, которые можно использовать вместо автоматизации Excel. Может быть, более новые версии Excel, как использование таким образом?

Ваш шаблон, по-видимому, сериализует все запросы, так что в любой момент времени может быть выполнен только один запрос (на основе Excel), что не очень желательно.

0 голосов
/ 03 февраля 2011

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

Точнее, я вижу две причины, по которым какой-то поток не вызывает End ():

  • Этот поток все еще выполняется, и это нормально, если вы не пытаетесь использовать ресурс, который в недоступномна данный момент и продолжайте пытаться.Поэтому, если вы попытаетесь остановить этот поток вручную (из другого потока, как вы сказали), то вы рискуете, что ваши данные будут в несогласованном состоянии - так же, как вызов Thread.Abort ().

  • Нормальный поток выполнения нарушается по исключению.Поэтому вам нужно очистить ресурсы и освободить этот монитор в простом блоке try / finally.

Обновление

Если его Excel недоступен при высокойзагрузить это имеет тенденцию бросать исключения, чтобы уведомить об этом.Эта тема обсуждалась недавно на Code Review.StackExchange и как справляться с такими ситуациями.

Еще одна стратегия, позволяющая справляться с ситуациями, когда вы не уверены, как долго вы будете ждать блокировки, заключается в использовании Monitor.TryEnter (object, ref bool) .Он был специально разработан для ситуаций, когда вы не хотите некоторое время ждать на мониторе, а вместо этого предпринять некоторые другие действия - поэтому вы вообще не будете блокировать.

0 голосов
/ 03 февраля 2011

Может быть, но вы бы просто скрывали реальную проблему.

Что вам действительно нужно выяснить, так это ПОЧЕМУ ваш замок не снимается. Если это C ++, вам, вероятно, следует использовать охрану (чтобы блокировки снимались, даже если что-то срабатывает).

...