简体   繁体   English

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

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

I have a user control that displays information from the database. 我有一个用户控件,可显示数据库中的信息。 This user control has to update these information constantly(let's say every 5 seconds). 该用户控件必须不断更新这些信息(假设每5秒钟更新一次)。 A few instances of this user control is generated programmatically during run time in a single page. 此用户控件的一些实例是在运行时以编程方式在单个页面中生成的。 In the code behind of this user control I added a code that sends a query to the database to get the needed information (which means every single instance of the user control is doing this). 在此用户控件的代码中,我添加了一个代码,该代码将查询发送到数据库以获取所需的信息(这意味着用户控件的每个实例都在执行此操作)。 But this seems to slow down the processing of queries so I am making a static class that will do the querying and store the information in its variables and let the instances of my user control access those variables. 但这似乎减慢了查询的处理速度,因此我正在制作一个静态类,该类将执行查询并将信息存储在其变量中,并让用户控制的实例访问这些变量。 Now I need this static class to do queries every 5 seconds to update its variables. 现在,我需要此静态类每5秒执行一次查询以更新其变量。 I tried using a new thread to do this but the variables don't seem to be updated since I always get a NullReferenceException whenever I access them from a different class. 我尝试使用新线程来执行此操作,但是变量似乎没有更新,因为无论何时从其他类访问它们,我总是会收到NullReferenceException。

Here's my static class: 这是我的静态课程:

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

And this is how I am trying to access the variables in my static class: 这就是我尝试访问静态类中的变量的方式:

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

Any help, please? 有什么帮助吗? Thanks 谢谢

Multiple threads problem 多线程问题

You have one thread that gets created for each and every call to SessionManager.Initialize . 您为SessionManager.Initialize每次调用都创建了一个线程。

That happens more than once in the lifetime of the process. 在过程的生命周期中,这种情况不止一次发生。 IIS recycles your app at some point, after a period of time should you have absolutely no requests. 在一段时间后,如果您绝对没有任何请求,IIS会在某个时候回收您的应用程序。 Until that happens, all your created threads continue to run. 在此之前,所有创建的线程将继续运行。

After the first PageLoad you will have one thread which updates stuff every 5 seconds. 在第一个PageLoad之后,您将有一个线程每5秒更新一次内容。

If you refresh the page again you'll have two threads, possibly with different offsets in time but each of which, doing the same thing at 5 second intervals. 如果再次刷新页面,您将有两个线程,它们的时间偏移可能不同,但是每个线程每隔5秒执行一次相同的操作。

You should atomically check to see if your background thread is started already. 您应该自动检查后台线程是否已经启动。 You need at least an extra bool static field and a object static field which you should use like a Monitor (using the lock keyword). 您至少需要一个额外的布尔静态字段和一个对象静态字段,它们应该像Monitor一样使用(使用lock关键字)。

You should also stop relying on volatile and simply using lock to make sure that other threads "observe" updated values for your static List<..> fields. 您还应该停止依赖volatile并简单地使用lock来确保其他线程“观察”静态List<..>字段的更新值。

It may be the case that the other threads don't observe a change field and thusly, for them, the field is still null - therefore you get the NullReferenceException . 这可能是因为其他线程不遵守变化领域正是如此,对于他们来说,本场依然如此null -所以你得到的NullReferenceException

About volatile 关于挥发物

Using volatile is bad, at least in .NET. 至少在.NET中,使用volatile不好。 There is a 90% chance that you think you know what it is doing and it's not true and there's a 99% chance that you feel relief because you used volatile and you aren't checking for other multitasking hazards the way you should. 您有90%的机会认为自己知道自己在做什么,但事实并非如此,并且有99%的机会让您感到放心,因为您使用了volatile ,并且没有按照应有的方式检查其他多任务危害。

RX to the rescue RX救援

I strongly suggest you take a look at this wonderful thing called Reactive Extensions . 我强烈建议您看一下称为Reactive Extensions的奇妙事物。

Believe me, a couple of days' research combined with the fact that you're in a perfect position to use RX will pay of, not just now but in the future as well. 相信我,经过两天的研究以及您处于使用RX的理想位置这一事实,不仅现在而且将来都会得到回报。

You get to keep your static class, but instead of materialised values that get stored within that class you create pipes that carry information. 您必须保留静态类,但是要创建存储信息的管道 ,而不是在该类中存储物化值。 The information flows when you want it to flow. 信息在您希望流动时流动。 You get to have subscribers to those pipes. 您必须拥有这些管道的订户 The number of subscribers does not affect the overall performance of your app. 订阅者数量不会影响您应用的整体性能。

Your app will be more scalable, and more robust. 您的应用程序将具有更大的可扩展性和更强大的功能。

Good luck! 祝好运!

There are few solution for this approach: One of them is: 这种方法的解决方案很少:其中之一是:

It's better in Global.asax in Application_start or Session_Start (depends on your case) create Thread to call your method: 最好在Application.start或Session_Start的Global.asax中创建(取决于您的情况)create Thread来调用您的方法:

Use below code : 使用以下代码:

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

Second solution is using Job Scheduler for ASP.NET (that's my ideal solution). 第二种解决方案是使用Job Scheduler for ASP.NET(这是我的理想解决方案)。 for more info you can check this link How to run Background Tasks in ASP.NET 有关更多信息,您可以检查此链接如何在ASP.NET中运行后台任务

and third solution is rewrite your static class as follow: 第三种解决方案是重写您的静态类,如下所示:

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

This is a solution that is a change in approach, but I kept the solution in Web Forms, to make it more directly applicable to your use case. 这是一种解决方案,只是方法上的变化,但我将该解决方案保留在Web窗体中,以使其更直接适用于您的用例。

SignalR is a technology that enables real-time, two way communication between server and clients (browsers), which can replace your static session data class. SignalR是一项使服务器和客户端(浏览器)之间能够进行实时双向通信的技术,可以代替您的静态会话数据类。 Below, I have implemented a simple example to demonstrate the concept. 下面,我实现了一个简单的示例来演示该概念。

As a sample, create a new ASP.NET Web Forms application and add the SignalR package from nuget. 作为示例,创建一个新的ASP.NET Web窗体应用程序,并从nuget中添加SignalR包。

Install-Package Microsoft.AspNet.SignalR

You will need to add a new Owin Startup class and add these 2 lines: 您将需要添加一个新的Owin Startup class并添加以下两行:

using Microsoft.AspNet.SignalR;

... and within the method ...并在方法内

app.MapSignalR();

Add some UI elements to Default.aspx : 将一些UI元素添加到Default.aspx

 <div class="jumbotron">

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

    <p class="stats">

    </p>

</div>

Add the following JavaScript to the Site.Master . 将以下JavaScript添加到Site.Master This code references signalr, and implement client-side event handlers and initiates contact with the signalr hub from the browser. 该代码引用了signalr,并实现了客户端事件处理程序,并从浏览器启动与signalr集线器的联系。 here's the code: 这是代码:

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

Finally, add a SignalR Hub to the solution and use this code for the SessionDataHub implementation: 最后,将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);
    }
}

For more information about SignalR, go to http://asp.net/signalr 有关SignalR的更多信息,请访问http://asp.net/signalr

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

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

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