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中的所有线程都可以访问此类。

现在,您可以在锁定语句中将所有代码封装在两个静态成员函数中。 请确保您在两个成员函数中锁定相同的锁变量。

此外,如果您不希望这些成员函数是静态的,那么您的类不必是静态的。 只要您的锁变量是静态的,您就可以了。

希望这可以帮助。