释放互斥锁

8
我有一个Web应用程序,需要利用应用程序缓存来存储数据(由于按请求一次次地获取数据的高开销)。请参见先前的帖子:https://stackoverflow.com/a/16961962/236860
这种方法似乎很有效,但我在网站错误中偶尔看到以下错误:
System.ApplicationException: Object synchronization method was called from an
unsynchronized block of code.
at System.Threading.Mutex.ReleaseMutex()
at InboxInsight.Web.Web_Controls.Twitter.TwitterFeed.GetTwitterData(HttpContext context)
at InboxInsight.Web.Web_Controls.Twitter.TwitterFeed.ProcessRequest(HttpContext context)
at System.Web.HttpApplication.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)

作为参考,这是代码块:

public string GetData(HttpContext context)
{
    var cache      = context.Cache;
    Mutex mutex    = null;
    string data    = (string)cache[CacheKey];

    // Start check to see if available on cache
    if (data == null)
    {
       try
       {
           // Lock base on resource key
           // (note that not all chars are valid for name)
           mutex = new Mutex(true, CacheKey);

           // Wait until it is safe to enter (someone else might already be
           // doing this), but also add 30 seconds max.
           mutex.WaitOne(30000);

           // Now let's see if some one else has added it...
           data = (string)cache[CacheKey];

           // They did, so send it...
           if (data != null)
           {
              return data;
           }


           // Still not there, so now is the time to look for it!
           data = GetSlowFeed(context);

           cache.Remove(CacheKey);
           cache.Add(CacheKey, data, null, GetExpiryDate(),
              TimeSpan.Zero, CacheItemPriority.Normal, null);
       }
       finally
       {
           // Release the Mutex.
           if (mutex != null)
           {
              mutex.ReleaseMutex();
           }
       }
    }

    return data;
}

经过我的调研,显示此问题是由一个进程线程试图释放它未创建的互斥锁造成的,但我不明白这怎么会发生。

有人能否建议我如何重新构建代码以避免这个问题?


我在你的另一个问题下添加了一条评论,不过这里也许更合适。目前有一些适用于 asp.net 的缓存提供程序,比如 AppFabric 和 Memcached,都非常出色。它们的功能强大到我觉得没必要自己重新造轮子了。 - NotMe
嗨,Chris,感谢您的建议。我现在已经查看了AppFabric和Memcached,但它们似乎不适用于我的应用程序,因为它正在共享托管环境中运行。作为分布式缓存应用程序,我认为这两者都需要在服务器/操作系统级别安装,不幸的是,这排除了它们在此应用程序中的使用。 - Neilski
1个回答

15

你没有处理mutex.WaitOne返回false的情况,即超时情况。如果WaitOne返回false,则你没有拥有mutex,因此不需要释放它。

bool iOwnTheMutex;

try {
    // set up mutex here...
    iOwnTheMutex = mutex.WaitOne(2000);
    if (iOwnTheMutex) {
       // do what you need to do
    }
}
finally {
    if (mutex != null && iOwnTheMutex) {
       mutex.ReleaseMutex();
    }
}    

这个答案在Metro应用程序中不再适用。即使在测试用例中,如果iOwnTheMutex为true,异常仍然会持续发生。我已经能够通过一个线程(UI)获取/释放互斥锁,而另一个线程(非UI)获取互斥锁并无法释放它来重新创建,而没有其他线程尝试获取它。 - Randall Becker
Mutex 的主要卖点是在进程之间进行同步。听起来你正在进程内进行同步。我相信你可以找到更适合这种情况的东西,比如最简单的 C# 中的 lock 关键字。 - Jack Hughes
我认为这不是一个安全的解决方案,因为bool类型不是线程安全的。 - Matt Wilko
1
"iOwnTheMutex" 是一个本地变量,没有其他线程/进程能够访问它。 - Jack Hughes
检查互斥锁是否被占用,然后尝试稍后释放它 - 这里存在竞态条件。 - user626528

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接