【问题标题】:Release lock from another thread than the Monitor's owner从监视器所有者以外的另一个线程释放锁
【发布时间】:2011-06-20 19:56:22
【问题描述】:

我在代码中有一个关键部分,它由两个函数调用分隔,比如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函数,请求结束调用End()。 excel进程总是有可能开始挂起。

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

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

【问题讨论】:

  • 为什么不在你的方法中设置一些东西来简单地 while(...) 直到达到某个阈值然后在那一刻爆发?
  • 不要试图修补代码中的错误。修复错误。
  • @Hans Passant:这不是为了修复我们的错误。我们通过 com interop 调用 Excel,我们无法确定 Excel 进程是否始终按预期工作。请注意,这是一个 Web 应用程序,因此无法处理这种情况是致命的。
  • @Aaron:你的意思是使用轮询而不是适当的锁定?
  • 基本上将其分解为典型的异步行为;所有异步操作都是相同的,因为您不知道它何时会返回。有了这个在你的应用程序中构建了一个关于所有异步操作的超时机制,如果在给定的阈值内没有收到返回,一种方法是轮询。

标签: .net multithreading locking deadlock


【解决方案1】:

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

我很想知道您为什么不使用lock 块以保证EnterExit,而不是直接使用Monitor

更新

阅读您的评论后,我强烈建议您组织您的代码,以便您可以本地化您的锁定,而不是在请求开始和请求结束时。如果您使用 lock,您仍然可以序列化对 Excel 的访问,但您可以保证 Enter Exit 被调用。

仅供参考 lockMonitor 在引擎盖下。

    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 的)请求 - 这似乎不太理想。

【讨论】:

  • 这是一个 ASP 应用程序。请求第一次调用 Excel 函数时调用 Start(),请求结束时调用 End()。有没有别的锁法?使用 Mutex 或 AutoResetEvent 可以实现我想要的吗?查看我的编辑。
【解决方案2】:

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

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

【讨论】:

    【解决方案3】:

    >> 如果某个线程出于某种原因没有调用 End(),那么我的整个进程就有问题了,因为每个线程都在等待这个 Monitor 被释放。

    准确地说,我看到一些线程不调用 End() 的两个原因:

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

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

    更新

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

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

    【讨论】:

    • 第一个原因是这样。我们在服务器上调用 excel,担心它可能会在重负载时开始阻塞。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-04-02
    • 2011-07-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多