ASP.NET中的全局变量

时间:2011-09-27 03:13:46

标签: c# asp.net asp.net-mvc multithreading locking

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

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

4 个答案:

答案 0 :(得分:3)

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

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

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

有很多方法可以解决这个问题。最好的方法是删除共享状态,这样每个线程都可以完全独立于其他线程运行,并且不需要锁定其他线程。但我会假设你已经考虑过这个并且它不实用。接下来要看的是mutexesmonitors。监视器使用起来有点简单,但只有当两个线程实际上位于同一个appdomain和相同的进程中时,它们才有效。这让我想到......

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

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

工人流程是您最可能的罪魁祸首。如果您遇到竞争条件,问题将非常罕见(当它出现时,甚至更难以调试)。如果你每次都看到它,我的钱就是多个工人流程。

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

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)将立即返回。如果您希望其他线程通常能够快速完成,您可以选择传递非零时间跨度;然后它会在放弃之前等待很长时间,这会给用户提供更好的体验,而不是立即进入“请稍后再试”错误消息。

答案 1 :(得分:1)

ASP.NET根据需要调整工作进程,不要对抗它。

使用数据库队列并让ASP.NET以最佳方式工作。在创建高度无法维护的代码的基础上,如果可以的话,你会扼杀服务器试图控制线程。如果您确实找到了控制它的方法,我无法想象您将遇到的错误类型。

答案 2 :(得分:0)

在上面的Edit案例中,您正在使用每个控制器实例创建该新对象,因此它只会锁定将在同一个线程上的代码 - 并且只有一个请求,因此不执行任何操作。

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

答案 3 :(得分:0)

实现此目的的唯一方法是使用互斥锁。 “锁定”语句是互斥锁的语法糖。

您应该将代码抽象为具有静态成员变量的静态类,以便app-domain中的所有线程都可以访问此类。

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

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

希望这会有所帮助。