[英]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.