繁体   English   中英

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

[英]How to make a static class update its own variables constantly?

我有一个用户控件,可显示数据库中的信息。 该用户控件必须不断更新这些信息(假设每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.Initialize每次调用都创建了一个线程。

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

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

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

您应该自动检查后台线程是否已经启动。 您至少需要一个额外的布尔静态字段和一个对象静态字段,它们应该像Monitor一样使用(使用lock关键字)。

您还应该停止依赖volatile并简单地使用lock来确保其他线程“观察”静态List<..>字段的更新值。

这可能是因为其他线程不遵守变化领域正是如此,对于他们来说,本场依然如此null -所以你得到的NullReferenceException

关于挥发物

至少在.NET中,使用volatile不好。 您有90%的机会认为自己知道自己在做什么,但事实并非如此,并且有99%的机会让您感到放心,因为您使用了volatile ,并且没有按照应有的方式检查其他多任务危害。

RX救援

我强烈建议您看一下称为Reactive Extensions的奇妙事物。

相信我,经过两天的研究以及您处于使用RX的理想位置这一事实,不仅现在而且将来都会得到回报。

您必须保留静态类,但是要创建存储信息的管道 ,而不是在该类中存储物化值。 信息在您希望流动时流动。 您必须拥有这些管道的订户 订阅者数量不会影响您应用的整体性能。

您的应用程序将具有更大的可扩展性和更强大的功能。

祝好运!

这种方法的解决方案很少:其中之一是:

最好在Application.start或Session_Start的Global.asax中创建(取决于您的情况)create Thread来调用您的方法:

使用以下代码:

var t = Task.Factory.StartNew(() => {
    while(true)
    {
      SessionManager.SetProperties();
      Task.Delay(5);
    }
});

第二种解决方案是使用Job Scheduler for ASP.NET(这是我的理想解决方案)。 有关更多信息,您可以检查此链接如何在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);
    }
}

这是一种解决方案,只是方法上的变化,但我将该解决方案保留在Web窗体中,以使其更直接适用于您的用例。

SignalR是一项使服务器和客户端(浏览器)之间能够进行实时双向通信的技术,可以代替您的静态会话数据类。 下面,我实现了一个简单的示例来演示该概念。

作为示例,创建一个新的ASP.NET Web窗体应用程序,并从nuget中添加SignalR包。

Install-Package Microsoft.AspNet.SignalR

您将需要添加一个新的Owin Startup class并添加以下两行:

using Microsoft.AspNet.SignalR;

...并在方法内

app.MapSignalR();

将一些UI元素添加到Default.aspx

 <div class="jumbotron">

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

    <p class="stats">

    </p>

</div>

将以下JavaScript添加到Site.Master 该代码引用了signalr,并实现了客户端事件处理程序,并从浏览器启动与signalr集线器的联系。 这是代码:

<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>

最后,将SignalR Hub添加到解决方案中,并将此代码用于SessionDataHub实现:

[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);
    }
}

有关SignalR的更多信息,请访问http://asp.net/signalr

链接到源代码: https : //lsscloud.blob.core.windows.net/downloads/WebApplication1.zip

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM