ASP.NET中的全局变量
我希望阻止一个线程输入代码,而另一个线程仍在执行该位代码:
我目前正在进行以下操作: global.asax.cs
private static bool _isProcessingNewIllustrationRequest; public static bool IsProcessingNewIllustrationRequest { get { return _isProcessingNewIllustrationRequest; } set { _isProcessingNewIllustrationRequest = value; } }
然后在MVC中:
public ActionResult CreateNewApplication() { if (!Global.IsProcessingNewIllustrationRequest) { Global.IsProcessingNewIllustrationRequest = true; // DO WORK... RUN CODE Global.IsProcessingNewIllustrationRequest = false; return View("Index", model); } else { // DISPLAY A MESSAGE THAT ANOTHER REQUEST IS IN PROCESS } }
但是,如果好像代码不起作用,因为两个线程(Chrome和Firefox)仍然同时执行代码。
更新
private Object thisLock = new Object(); public ActionResult CreateApplication() { ILog log = LogManager.GetLogger(typeof(Global)); string aa = this.ControllerContext.HttpContext.Session.SessionID; log.Info("MY THREAD: " + aa); lock (thisLock) { Thread.Sleep(8000); DO SOME STUFF } }
即使使用日志,线程2(Firefox会话)仍会进入代码,而session1(Chrome)使用锁执行它。 我错过了什么?
我可以看到两个明显的原因,为什么这段代码不会做你想要的。
问题#1:它不是线程安全的。 每当有人连接到您的服务器时,该请求将在一个线程中运行,如果有多个请求进入,那么多个线程都将同时运行。 几个线程可以同时读取标志,所有人都看到它是错误的,所以所有人都进入他们的if
块并且所有人都做了他们的工作,这正是你想要阻止的。
这是一场“竞争条件”(结果取决于谁赢得比赛)。 布尔变量不足以解决竞争条件。
有很多方法可以解决这个问题。 最好的方法是删除共享状态,这样每个线程都可以完全独立于其他线程运行,并且不需要锁定其他线程; 但我会假设你已经考虑过了,这是不切实际的。 接下来要看的是互斥和监视器 。 监视器使用起来有点简单,但只有当两个线程实际上位于同一个appdomain和相同的进程中时,它们才有效。 这让我…
问题2:两个线程可能不在同一个进程中。 最新版本的IIS将启动多个单独的工作进程来处理Web请求。 每个进程都有自己的地址空间,这意味着每个进程(技术上每个进程中的每个appdomain)都有自己的“全局”变量副本!
事实上,如果您正在构建一个真正高流量的Web站点,则不同的工作进程甚至可能无法在同一台计算机上运行。
工人流程是你最可能的罪魁祸首。 如果您遇到竞争条件,问题将非常罕见(当它出现时更难以调试)。 如果你每次都看到它,我的钱就是多个工人流程。
让我们假设所有工作进程都在同一台服务器上(毕竟,如果你有那种需要可以扩展到多个Web服务器的应用程序的预算,你就会比我更了解multithreading编程)。 如果所有内容都在同一台服务器上,您可以使用命名的互斥锁来协调不同的应用程序域和进程。 像这样的东西:
public static Mutex _myMutex = new Mutex(false, @"Global\SomeUniqueName"); public ActionResult CreateNewApplication() { if (_myMutex.WaitOne(TimeSpan.Zero)) { // DO WORK... RUN CODE _myMutex.ReleaseMutex(); return View("Index", model); } else { // DISPLAY A MESSAGE THAT ANOTHER REQUEST IS IN PROCESS } }
如果另一个线程拥有互斥锁, WaitOne(TimeSpan.Zero)
将立即返回。 如果您希望其他线程通常能够快速完成,您可以选择传递非零时间跨度; 那么它会在放弃之前等待很长时间,这会给用户提供更好的体验,而不是立即进入“请稍后再试”错误消息。
ASP.NET根据需要调整工作进程,不要对抗它。
使用数据库队列并让ASP.NET以最佳方式工作。 在创建高度不可维护的代码的基础上,如果可以的话,你会扼杀服务器试图控制线程。 如果你确实找到了控制它的方法,我无法想象你会遇到的错误类型。
在上面的Edit案例中,您正在使用每个控制器实例创建该新对象,因此它只会锁定将在同一个线程上的代码 – 并且只有一个请求,因此不执行任何操作。
尝试在应用程序启动时记录您的全局类变量,以便运行静态初始化程序,并且您知道事情是“设置”。 然后使用此全局类中存在的对象(可以是任何东西 – 一个字符串)并将其用作锁变量。
实现此目的的唯一方法是使用互斥锁。 “lock”语句是互斥锁的语法糖。
您应该将代码抽象为具有静态成员变量的静态类,以便app-domain中的所有线程都可以访问此类。
现在,您可以在锁定语句中将所有代码封装在两个静态成员函数中。 请确保您在两个成员函数中锁定相同的锁变量。
此外,如果您不希望这些成员函数是静态的,那么您的类不必是静态的。 只要您的锁变量是静态的,您就可以了。
希望这可以帮助。