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