C# 如何使静态类不断更新自己的变量?

C# 如何使静态类不断更新自己的变量?,c#,asp.net,multithreading,C#,Asp.net,Multithreading,我有一个显示数据库信息的用户控件。这个用户控件必须不断更新这些信息(比如说每5秒一次)。此用户控件的一些实例是在运行时在单个页面中以编程方式生成的。在这个用户控件的代码隐藏中,我添加了一个向数据库发送查询以获取所需信息的代码(这意味着用户控件的每个实例都在这样做)。但这似乎减慢了查询的处理速度,因此我创建了一个静态类,它将执行查询并将信息存储在其变量中,并让用户的实例控制访问这些变量。现在我需要这个静态类每5秒进行一次查询,以更新其变量。我尝试使用一个新线程来实现这一点,但变量似乎没有更新,因为

我有一个显示数据库信息的用户控件。这个用户控件必须不断更新这些信息(比如说每5秒一次)。此用户控件的一些实例是在运行时在单个页面中以编程方式生成的。在这个用户控件的代码隐藏中,我添加了一个向数据库发送查询以获取所需信息的代码(这意味着用户控件的每个实例都在这样做)。但这似乎减慢了查询的处理速度,因此我创建了一个静态类,它将执行查询并将信息存储在其变量中,并让用户的实例控制访问这些变量。现在我需要这个静态类每5秒进行一次查询,以更新其变量。我尝试使用一个新线程来实现这一点,但变量似乎没有更新,因为每当我从不同的类访问它们时,总是会得到一个NullReferenceException

这是我的静态类:

public static class SessionManager
{
    public static volatile List<int> activeSessionsPCIDs;
    public static volatile List<int> sessionsThatChangedStatus;
    public static volatile List<SessionObject> allSessions;

    public static void Initialize() {
        Thread t = new Thread(SetProperties);
        t.Start();
    }

    public static void SetProperties() {
        SessionDataAccess sd = new SessionDataAccess();
        while (true) {
            allSessions = sd.GetAllSessions();
            activeSessionsPCIDs = new List<int>();
            sessionsThatChangedStatus = new List<int>();
            foreach (SessionObject session in allSessions) {
                if (session.status == 1) { //if session is active
                    activeSessionsPCIDs.Add(session.pcid);
                }
                if (session.status != session.prevStat) { //if current status doesn't match the previous status
                    sessionsThatChangedStatus.Add(session.pcid);
                }
            }
            Thread.Sleep(5000);
        }
    }
protected void Page_Load(object sender, EventArgs e)
    {
        SessionManager.Initialize();
        loadSessions();
    }

    private void loadSessions()
    { // refresh the current_sessions table
        List<int> pcIds = pcl.GetPCIds(); //get the ids of all computers
        foreach (SessionObject s in SessionManager.allSessions)
        {
            SessionInfo sesInf = (SessionInfo)LoadControl("~/UserControls/SessionInfo.ascx");
            sesInf.session = s;
            pnlMonitoring.Controls.Add(sesInf);
        } 
    }
公共静态类SessionManager
{
公共静态易失性列表ActiveSessionsPCID;
更改状态的公共静态易失性列表会话;
公开静态易变列表所有会话;
公共静态void Initialize(){
线程t=新线程(设置属性);
t、 Start();
}
公共静态void SetProperties(){
SessionDataAccess sd=新的SessionDataAccess();
while(true){
allSessions=sd.GetAllSessions();
activeSessionsPCIDs=新列表();
sessionsThatChangedStatus=新列表();
foreach(所有会话中的会话对象会话){
如果(session.status==1){//如果会话处于活动状态
activeSessionsPCIDs.Add(session.pcid);
}
if(session.status!=session.prevStat){//如果当前状态与以前的状态不匹配
sessionsThatChangedStatus.Add(session.pcid);
}
}
睡眠(5000);
}
}
这就是我试图访问静态类中变量的方式:

public static class SessionManager
{
    public static volatile List<int> activeSessionsPCIDs;
    public static volatile List<int> sessionsThatChangedStatus;
    public static volatile List<SessionObject> allSessions;

    public static void Initialize() {
        Thread t = new Thread(SetProperties);
        t.Start();
    }

    public static void SetProperties() {
        SessionDataAccess sd = new SessionDataAccess();
        while (true) {
            allSessions = sd.GetAllSessions();
            activeSessionsPCIDs = new List<int>();
            sessionsThatChangedStatus = new List<int>();
            foreach (SessionObject session in allSessions) {
                if (session.status == 1) { //if session is active
                    activeSessionsPCIDs.Add(session.pcid);
                }
                if (session.status != session.prevStat) { //if current status doesn't match the previous status
                    sessionsThatChangedStatus.Add(session.pcid);
                }
            }
            Thread.Sleep(5000);
        }
    }
protected void Page_Load(object sender, EventArgs e)
    {
        SessionManager.Initialize();
        loadSessions();
    }

    private void loadSessions()
    { // refresh the current_sessions table
        List<int> pcIds = pcl.GetPCIds(); //get the ids of all computers
        foreach (SessionObject s in SessionManager.allSessions)
        {
            SessionInfo sesInf = (SessionInfo)LoadControl("~/UserControls/SessionInfo.ascx");
            sesInf.session = s;
            pnlMonitoring.Controls.Add(sesInf);
        } 
    }
受保护的无效页面加载(对象发送方,事件参数e)
{
初始化();
loadSessions();
}
私有void loadSessions()
{//刷新当前的_sessions表
List pcIds=pcl.GetPCIds();//获取所有计算机的ID
foreach(SessionManager.allSessions中的sessionobjects)
{
SessionInfo sesInf=(SessionInfo)LoadControl(“~/UserControls/SessionInfo.ascx”);
sesInf.session=s;
pnlMonitoring.Controls.Add(sesInf);
} 
}

有什么帮助吗?谢谢

这种方法几乎没有解决方案: 其中之一是:

最好在Global.asax中的应用程序_start或会话_start(取决于您的情况)中创建线程来调用您的方法:

使用以下代码:

var t = Task.Factory.StartNew(() => {
    while(true)
    {
      SessionManager.SetProperties();
      Task.Delay(5);
    }
});
第二种解决方案是使用ASP.NET作业调度程序(这是我的理想解决方案)。 有关更多信息,请查看此链接

第三种解决方案是重写静态类,如下所示:

public static class SessionManager
{
 public static volatile List<int> activeSessionsPCIDs;
 public static volatile List<int> sessionsThatChangedStatus;
 public static volatile List<SessionObject> allSessions;

 static SessionManager()
 {
   Initialize();
 }

public static void Initialize() {
    var t = Task.Factory.StartNew(() => {
       while(true)
       {
         SetProperties();
         Task.Delay(5);
       }
     });
}

public static void SetProperties() {
    SessionDataAccess sd = new SessionDataAccess();
    while (true) {
        allSessions = sd.GetAllSessions();
        activeSessionsPCIDs = new List<int>();
        sessionsThatChangedStatus = new List<int>();
        foreach (SessionObject session in allSessions) {
            if (session.status == 1) { //if session is active
                activeSessionsPCIDs.Add(session.pcid);
            }
            if (session.status != session.prevStat) { //if current status doesn't match the previous status
                sessionsThatChangedStatus.Add(session.pcid);
            }
        }
        Thread.Sleep(5000);
    }
}
公共静态类SessionManager
{
公共静态易失性列表ActiveSessionsPCID;
更改状态的公共静态易失性列表会话;
公开静态易变列表所有会话;
静态会话管理器()
{
初始化();
}
公共静态void Initialize(){
var t=Task.Factory.StartNew(()=>{
while(true)
{
SetProperties();
任务延迟(5);
}
});
}
公共静态void SetProperties(){
SessionDataAccess sd=新的SessionDataAccess();
while(true){
allSessions=sd.GetAllSessions();
activeSessionsPCIDs=新列表();
sessionsThatChangedStatus=新列表();
foreach(所有会话中的会话对象会话){
如果(session.status==1){//如果会话处于活动状态
activeSessionsPCIDs.Add(session.pcid);
}
if(session.status!=session.prevStat){//如果当前状态与以前的状态不匹配
sessionsThatChangedStatus.Add(session.pcid);
}
}
睡眠(5000);
}
}

多线程问题

您有一个为每次调用
SessionManager.Initialize
创建的线程

这在流程的生命周期中不止一次发生。 IIS会在某个时间点回收您的应用程序,如果一段时间后您完全没有任何请求。 在此之前,所有创建的线程都将继续运行

在第一次
PageLoad
之后,您将有一个线程每5秒更新一次内容

如果您再次刷新页面,您将有两个线程,可能具有不同的时间偏移量,但每个线程以5秒的间隔执行相同的操作

您应该以原子方式检查后台线程是否已经启动。您至少需要一个额外的bool静态字段和一个对象静态字段,该字段应该像
监视器一样使用(使用
锁定
关键字)

您还应该停止依赖volatile,只需使用
lock
来确保其他线程“观察”静态
列表
字段的更新值

可能是其他线程没有观察到更改字段,因此,对于它们来说,该字段仍然是
null
——因此您会得到
NullReferenceException

关于volatile

使用
volatile
是不好的,至少在.NET中是这样。有90%的几率你认为你知道它在做什么,但这不是真的,有99%的几率你会因为你使用了
volatile
而没有按你应该的方式检查其他多任务危害而感到轻松

RX救援

我强烈建议你去看看这个奇妙的东西
app.MapSignalR();
 <div class="jumbotron">

    <H3 class="MyName">Loading...</H3>

    <p class="stats">

    </p>

</div>
<script src="Scripts/jquery.signalR-2.2.0.min.js"></script>
<script src="signalr/hubs"></script>
<script >
    var hub = $.connection.sessiondata;
    hub.client.someOneJoined = function (name) {
        var current = $(".stats").text();
        current = current + '\nuser ' + name + ' joined.';
        $(".stats").text(current);
    };
    hub.client.myNameIs = function (name) {
        $(".MyName").text("Your user id: " + name);
    };

    $.connection.hub.start().done(function () { });
</script>
[HubName("sessiondata")]
public class SessionDataHub : Hub
{
    private ObservableCollection<string> sessions = new ObservableCollection<string>();

    public SessionDataHub()
    {
        sessions.CollectionChanged += sessions_CollectionChanged;
    }

    private void sessions_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
        if (e.Action == NotifyCollectionChangedAction.Add)
        {
            Clients.All.someOneJoined(e.NewItems.Cast<string>().First());
        }
    }

    public override Task OnConnected()
    {
        return Task.Factory.StartNew(() =>
        {
            var youAre = Context.ConnectionId;
            Clients.Caller.myNameIs(youAre);
            sessions.Add(youAre);
        });
    }

    public override Task OnDisconnected(bool stopCalled)
    {
        // TODO: implement this as well. 
        return base.OnDisconnected(stopCalled);
    }
}