从Monitor的所有者之外的另一个线程释放锁定

时间:2011-02-03 17:12:20

标签: .net multithreading locking deadlock

我在代码中有一个关键部分,它由两个函数调用分隔,比如说Start()End()。他们在执行期间使用Monitor来阻止其他线程。现在我的问题是,如果某个线程由于某种原因没有调用End(),我的整个过程都会遇到麻烦,因为每个线程都在等待这个Monitor被释放。

当然我可以使用TryEnter超时,这样我就不会永远等待,但这不会释放被阻止的Monitor,所以我的程序每次都会从这个时间开始进入此超时上。

如果给定的超时结束,有没有办法从另一个线程释放阻塞Monitor

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

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

修改 我们通过com interop调用Excel,我们无法确定Excel进程是否始终按预期工作。请注意,这是一个Web应用程序,因此无法处理该案例是致命的。第一次调用Start(),请求调用excel函数,在Request end调用End()。 excel进程总是有可能开始挂起。

EDIT2: 我现在想的是将ent锁的所有者存储在一个变量中并且在死锁上我可以杀死这个线程。这不会释放锁吗?

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

3 个答案:

答案 0 :(得分:2)

唯一可以释放锁的线程是拥有锁的线程。所以不,你不能直接从另一个线程“解锁”监视器 - 这在设计上是不可能的。如果你能够这样做,其他线程将能够通过在它们实际上没有它时释放它来覆盖锁的语义。

我很想知道为什么你不使用lock块来保证EnterExit,而不是直接使用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的访问权限?我猜你正在为你的ASP.Net应用程序使用Excel自动化服务器端。除非事情发生了显着变化,否则这至少在几年后就会非常麻烦。如果你拿出锁并且Excel挂起,你就会被塞进去。可以使用第三方解决方案代替Excel自动化。也许更新版本的Excel就像以这种方式使用?

您的模式似乎序列化了所有请求,因此任何时候都只能执行一个(基于Excel)请求 - 这似乎不是很理想。

答案 1 :(得分:0)

也许,但你只是隐藏了真正的问题。

你真正需要弄清楚的是为什么你的锁没有被释放。如果这是C ++,你可能应该使用一个后卫(这样即使有东西抛出锁也会被释放)。

答案 2 :(得分:0)

&GT;&GT;如果某个线程由于某种原因没有调用End(),我的整个过程都会遇到麻烦,因为每个线程都在等待这个Monitor被释放。

让我们准确一点,我看到一些线程没有调用End()的两个原因:

  • 此线程仍在执行且状态正常,除非您尝试使用目前不可用的资源并继续尝试。因此,如果您尝试手动停止此线程(从您说的另一个线程),那么您将面临数据处于不一致状态的危险 - 就像调用Thread.Abort()一样。

  • 正常的执行流程被异常打破。因此,您需要清理资源并在一个简单的try / finally块中释放此监视器。

<强>更新

如果Excel在高负载下不可用,则往往会抛出异常来通知它。最近在Code Review. StackExchange讨论了这个主题以及如何处理这种情况。

当您不确定等待锁定的时间时,处理情况的另一种策略是使用Monitor.TryEnter(object, ref bool)。它专门针对您不想在显示器上等待一段时间而是采取其他一些操作的情况而设计 - 因此您根本不会阻止。