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