ASP.NET中的全局变量

本文关键字:全局变量 NET ASP | 更新日期: 2023-09-27 18:09:38

我希望在另一个线程仍在执行该代码时阻止线程输入代码:

我目前正在进行以下操作: 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)正在执行它的锁。我错过了什么?

ASP.NET中的全局变量

我可以看到两个明显的原因,为什么这段代码不能达到您想要的效果。

问题1:它不是线程安全的。每次有人连接到您的服务器时,该请求将在一个线程中运行,如果有多个请求进入,则多个线程将同时运行。几个线程可以同时读取这个标志,所有线程都看到它是假的,所以所有线程都进入它们的if块,所有线程都在做它们的工作,这正是你想要阻止的。

这是一个"竞赛条件"(结果取决于谁赢得了比赛)。布尔变量不足以解决竞争条件。

有很多方法可以解决这个问题。最好的方法是删除共享状态,这样每个线程就可以完全独立于其他线程运行,而不需要将其他线程锁在外面;但我假设你已经考虑过了,这是不现实的。接下来要看的是互斥锁和监视器。监视器的使用稍微简单一些,但它们只有在两个线程实际上位于相同的应用域和进程中时才能工作。这使我想到……

问题#2:两个线程可能不在在同一个进程中。最新版本的IIS将启动多个单独的工作进程来处理Web请求。每个进程都有自己的地址空间,这意味着每个进程(从技术上讲,每个进程中的每个应用域)都有自己的"全局"变量副本!

事实上,如果您正在构建一个真正高流量的Web站点,不同的工作进程甚至可能不会在同一台计算机上运行。

工作进程是最可能的罪魁祸首。如果您面对的是竞态条件,那么这个问题将非常罕见(甚至当它出现时更难调试)。如果你每次都看到它,我的钱在多个工作进程上。

让我们假设所有的工作进程都在同一台服务器上(毕竟,如果您有足够的预算,需要应用程序可以扩展到多个Web服务器,那么您应该比我更了解多线程编程)。如果所有内容都在同一台服务器上,则可以使用命名互斥锁来协调不同的应用域和进程。像这样:

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)将立即返回。如果你期望另一个线程通常会很快完成,你可以选择传递一个非零的时间跨度;然后它会在放弃之前等待很长时间,这将给用户更好的体验,而不是立即失败到"请稍后再试"的错误信息。

. NET根据需要对工作进程进行假脱机,不要与之抗争。

使用数据库队列,让ASP。NET工作最佳。如果可以的话,在创建高度不可维护的代码的基础上,您将阻塞试图控制线程的服务器。如果你真的找到了控制它的方法,我无法想象你会遇到什么样的bug。

在上面的编辑案例中,您正在创建每个控制器实例的新对象,因此它只会锁定同一线程上的代码-并且只有一个请求,因此什么也不做。

尝试在应用程序启动时记录全局类变量,以便静态初始化器运行,并且您知道事情已经"设置"。然后使用这个全局类中存在的一个对象(它可以是任何东西——字符串),并将其用作锁变量。

实现这一点的唯一方法是使用互斥锁。"lock"语句是互斥锁的语法糖。

你应该把你的代码抽象成一个静态类,它有一个静态成员变量,这样你的应用程序域中的所有线程都可以访问这个类。

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

同样,如果你不希望这些成员函数是静态的,那么你的类也不必是静态的。只要锁变量是静态的,就不会有问题。

希望这对你有帮助。