C#多线程窗口刷新问题 最近边写一个程序,数据库更新比较慢,所以单开了一个线程,主线程里有个while循环,用来不断刷新进度条显示更新到哪一条。程序不和用户交互没有问题,但是只要用户切换一下窗口,也就是进度条窗口失去焦点,在激活窗体后进度条和text就不刷新了不知道是什么问题,不是两个线程么,为什么切换一下窗口就暂停refresh了?怎么办才能办到切换也刷新啊?小白请大大赐教~ 解决方案 » 免费领取超大流量手机卡,每月29元包185G流量+100分钟通话, 中国电信官方发货 1、你的线程在显示当前的进度,也就是写text属性和设置进度条的代码,要以委托来执行。2、建议你降低线程的级别为belownormal。 根本原因在于while的循环不止,造成不能及时刷新进度条,所以如果没有执行完时,看似进度条停止了,一旦执行完成,你会发现进度条马上变到100%.建议用计器timer,在它的Tick事件中执行更新进度条的代码,只要设置一个适当的时间间隔interval即可。或者把更新进度条的代码也写在一个线程中,但可能会发生“线程间操作无效:”错误,可以在初始化时或load事件中写上这一句:Control.CheckForIllegalCrossThreadCalls = false; 更新数据库的线程代码:private void UpdateProgress() { string AllUpCommString = "Select 代码,名称 from stockSelect"; string StockID, StockName; SqlConnection ConnNew = new SqlConnection(Constant.ConnStock); SqlCommand AllUpCmd = new SqlCommand(AllUpCommString, ConnNew); ConnNew.Open(); SqlDataReader MyDataReader = AllUpCmd.ExecuteReader(); while (MyDataReader.Read()) { StockID = (string)MyDataReader[0]; StockName = (string)MyDataReader[1]; UpdateStock(StockID); lock ((object) PStruct) { PStruct.ProgressStock = "正在更新" + StockName + "(" + StockID + ")"; PStruct.ProgressSum++; } UpdateAllThread.Suspend(); //if (StockID == "000007") // break; } }更新进度条部分的代码(主线程):private void 全部股票更新ToolStripMenuItem_Click(object sender, EventArgs e) { AllStockUpdate AllUD = new AllStockUpdate(); AllUD.Text = "更新所有股票"; AllUD.label1.Text = "正在更新"; AllUD.Tmp = UpdateAllThread; AllUD.Show(); AllUD.progressBar1.Show(); Application.DoEvents(); AllUD.progressBar1.Maximum = TotalStockNum; AllUD.progressBar1.Minimum = 0; AllUD.progressBar1.Step = 1; UpdateAllThread = new Thread(new ThreadStart(UpdateProgress)); UpdateAllThread.Priority = ThreadPriority.BelowNormal; UpdateAllThread.Start(); while (PStruct.ProgressSum <= TotalStockNum) { AllUD.Refresh(); if(UpdateAllThread.ThreadState == ThreadState.Suspended||UpdateAllThread.ThreadState == ThreadState.SuspendRequested) { lock ((object)PStruct) { AllUD.progressBar1.Value = PStruct.ProgressSum; AllUD.label1.Text = PStruct.ProgressStock; } //AllUD.Refresh(); UpdateAllThread.Resume(); MyDataGridView.Refresh(); menuStrip1.Refresh(); } if(UpdateAllThread.ThreadState == ThreadState.Stopped) break; //Thread.Sleep(10); } AllUD.Hide(); String SqlCmdNew = "select NUM,代码,名称,综合指数,更新日期,现价,港股价格,溢价率 from stockSelect"; SqlCommand MySqlCommNew = new SqlCommand(SqlCmdNew, Conn); SqlDataAdapter MyDataAdNew = new SqlDataAdapter(MySqlCommNew); MyDataSet.Clear(); MyDataAdNew.Fill(MyDataSet, "StockInfo"); this.MyDataGridView.DataSource = MyDataSet.Tables["StockInfo"]; MyDataGridView.Columns[0].Visible = false; Conn.Close(); AllUD.Dispose(); } 可是我的while 循环是用来刷新进度条的refresh()啊,怎么还能把刷新的过程给停止了?而且我发现我进度条那个窗口的控件都不能响应,任何按钮很是困惑啊。。 while (PStruct.ProgressSum <= TotalStockNum) { AllUD.Refresh(); if(UpdateAllThread.ThreadState == ThreadState.Suspended||UpdateAllThread.ThreadState == ThreadState.SuspendRequested) { lock ((object)PStruct) { AllUD.progressBar1.Value = PStruct.ProgressSum; AllUD.label1.Text = PStruct.ProgressStock; } //AllUD.Refresh(); UpdateAllThread.Resume(); MyDataGridView.Refresh(); menuStrip1.Refresh(); } if(UpdateAllThread.ThreadState == ThreadState.Stopped) break; //Thread.Sleep(10); } //感觉问题应该在这里,因为你只判断了线程的三种状态,其实线程状态还有很多其实三楼的建议不错,应该将刷新进度条放在一个单独线程中或采用定时器Timer完成 你的线程是窗体的辅线程辅线程运行会自动阻塞主线程怎么办吧,就要辅线程有机会给主线程执行doevent或SLEEP lz在“全部股票更新ToolStripMenuItem_Click”方法中的while循环可能是想循环监控存储进度的结构体PStruct的值,以达到同步显示的目的。我的建议:lz在UpdateProgress()方法中就可以用委托来刷新显示进度条的显示。delegate void dlt_CallProgress(string strText,int intProgress);dlt_CallProgress Invoke_CallProgress;void CallProgress(string strText,int intProgress){ AllUD.progressBar1.Value = strText; AllUD.label1.Text = intProgress;}然后在UpdateProgress()方法的while循环中 while (MyDataReader.Read()) { StockID = (string)MyDataReader[0]; StockName = (string)MyDataReader[1]; UpdateStock(StockID); lock ((object) PStruct) { PStruct.ProgressStock = "正在更新" + StockName + "(" + StockID + ")"; PStruct.ProgressSum++; } UpdateAllThread.Suspend(); //if (StockID == "000007") // break; }改动为 while (MyDataReader.Read()) { StockID = (string)MyDataReader[0]; StockName = (string)MyDataReader[1]; UpdateStock(StockID); //lock ((object) PStruct) //{ // PStruct.ProgressStock = "正在更新" + StockName + "(" + StockID + ")"; // PStruct.ProgressSum++; //} Invoke_CallProgress myICP = new Invoke_CallProgress(CallProgress); PStruct.ProgressSum++; BeginInvoke(myICP,"正在更新" + StockName + "(" + StockID + ")",PStruct.ProgressSum); UpdateAllThread.Suspend(); //if (StockID == "000007") // break; } 最后lz在“全部股票更新ToolStripMenuItem_Click”方法中的 while (PStruct.ProgressSum <= TotalStockNum) { AllUD.Refresh(); if(UpdateAllThread.ThreadState == ThreadState.Suspended||UpdateAllThread.ThreadState == ThreadState.SuspendRequested) { lock ((object)PStruct) { AllUD.progressBar1.Value = PStruct.ProgressSum; AllUD.label1.Text = PStruct.ProgressStock; } //AllUD.Refresh(); UpdateAllThread.Resume(); MyDataGridView.Refresh(); menuStrip1.Refresh(); } if(UpdateAllThread.ThreadState == ThreadState.Stopped) break; //Thread.Sleep(10); } 这段代码就可以注释掉了,在主进程中开启while循环来刷新控件的显示,及时是加上了sleep也会使cpu占用率很高。<以上代码未经测试,是直接写在回复框中的,lz可参考思路,自行测试>补充建议:1、在UpdateProgress()方法中,若要调用窗体控件,一定要使用Invoke委托来调用。2、建议lz的代码分分层:数据库操作的部分用一个类、业务代码独立为一个单独的方法、界面的按钮事件仅调用独立的业务方法。否则,如果将来需求有所改动,你的工作量可大了去了。 恩,貌似就是这个问题,我试了下,就是while循环引起的,貌似while循环会占用ui的很多资源,然后就假死了,呵呵~谢谢~你代码很有用~结贴去了 精通WPF控件樣式(模板)的請進...TabControl 如何利用C#数组中的reserve进行数字交换 c# 类型能重命名? 新手求教!急!急!急! 在form2登录后才显示form1应该怎么做 如何绑定报表数据源!!!!!!!! 无法将类型“double”隐式转换为“int” 安装完成后,要求有个有个提示页面。提示如何使用后台.我用的是nisi老大们帮帮忙。 如何实现如WINAMP的插件技术? 为何添加WEB引用不成功? C#传数组参数到C++的类库中 急 !!!在线等 Button btn=new Button();//如何在创建对象时声明其名字和坐标等信息?
2、建议你降低线程的级别为belownormal。
建议用计器timer,在它的Tick事件中执行更新进度条的代码,只要设置一个适当的时间间隔interval即可。
或者把更新进度条的代码也写在一个线程中,但可能会发生“线程间操作无效:”错误,可以在初始化时或load事件中写上这一句:Control.CheckForIllegalCrossThreadCalls = false;
private void UpdateProgress()
{
string AllUpCommString = "Select 代码,名称 from stockSelect";
string StockID, StockName;
SqlConnection ConnNew = new SqlConnection(Constant.ConnStock);
SqlCommand AllUpCmd = new SqlCommand(AllUpCommString, ConnNew);
ConnNew.Open();
SqlDataReader MyDataReader = AllUpCmd.ExecuteReader(); while (MyDataReader.Read())
{
StockID = (string)MyDataReader[0];
StockName = (string)MyDataReader[1];
UpdateStock(StockID);
lock ((object) PStruct)
{
PStruct.ProgressStock = "正在更新" + StockName + "(" + StockID + ")";
PStruct.ProgressSum++;
}
UpdateAllThread.Suspend();
//if (StockID == "000007")
// break;
} }更新进度条部分的代码(主线程):
private void 全部股票更新ToolStripMenuItem_Click(object sender, EventArgs e)
{
AllStockUpdate AllUD = new AllStockUpdate();
AllUD.Text = "更新所有股票";
AllUD.label1.Text = "正在更新"; AllUD.Tmp = UpdateAllThread; AllUD.Show();
AllUD.progressBar1.Show();
Application.DoEvents();
AllUD.progressBar1.Maximum = TotalStockNum;
AllUD.progressBar1.Minimum = 0;
AllUD.progressBar1.Step = 1; UpdateAllThread = new Thread(new ThreadStart(UpdateProgress));
UpdateAllThread.Priority = ThreadPriority.BelowNormal;
UpdateAllThread.Start();
while (PStruct.ProgressSum <= TotalStockNum)
{
AllUD.Refresh();
if(UpdateAllThread.ThreadState == ThreadState.Suspended||UpdateAllThread.ThreadState == ThreadState.SuspendRequested)
{
lock ((object)PStruct)
{ AllUD.progressBar1.Value = PStruct.ProgressSum;
AllUD.label1.Text = PStruct.ProgressStock;
}
//AllUD.Refresh();
UpdateAllThread.Resume();
MyDataGridView.Refresh();
menuStrip1.Refresh();
}
if(UpdateAllThread.ThreadState == ThreadState.Stopped)
break;
//Thread.Sleep(10);
}
AllUD.Hide(); String SqlCmdNew = "select NUM,代码,名称,综合指数,更新日期,现价,港股价格,溢价率 from stockSelect";
SqlCommand MySqlCommNew = new SqlCommand(SqlCmdNew, Conn);
SqlDataAdapter MyDataAdNew = new SqlDataAdapter(MySqlCommNew);
MyDataSet.Clear();
MyDataAdNew.Fill(MyDataSet, "StockInfo");
this.MyDataGridView.DataSource = MyDataSet.Tables["StockInfo"];
MyDataGridView.Columns[0].Visible = false;
Conn.Close(); AllUD.Dispose();
}
可是我的while 循环是用来刷新进度条的refresh()啊,怎么还能把刷新的过程给停止了?而且我发现我进度条那个窗口的控件都不能响应,任何按钮很是困惑啊。。
while (PStruct.ProgressSum <= TotalStockNum)
{
AllUD.Refresh();
if(UpdateAllThread.ThreadState == ThreadState.Suspended||UpdateAllThread.ThreadState == ThreadState.SuspendRequested)
{
lock ((object)PStruct)
{ AllUD.progressBar1.Value = PStruct.ProgressSum;
AllUD.label1.Text = PStruct.ProgressStock;
}
//AllUD.Refresh();
UpdateAllThread.Resume();
MyDataGridView.Refresh();
menuStrip1.Refresh();
}
if(UpdateAllThread.ThreadState == ThreadState.Stopped)
break;
//Thread.Sleep(10);
}
//感觉问题应该在这里,因为你只判断了线程的三种状态,其实线程状态还有很多其实三楼的建议不错,应该将刷新进度条放在一个单独线程中或采用定时器Timer完成
辅线程运行会自动阻塞主线程
怎么办吧,就要辅线程有机会给主线程执行
doevent或SLEEP
lz在UpdateProgress()方法中就可以用委托来刷新显示进度条的显示。delegate void dlt_CallProgress(string strText,int intProgress);
dlt_CallProgress Invoke_CallProgress;void CallProgress(string strText,int intProgress)
{
AllUD.progressBar1.Value = strText;
AllUD.label1.Text = intProgress;
}然后
在UpdateProgress()方法的while循环中
while (MyDataReader.Read())
{
StockID = (string)MyDataReader[0];
StockName = (string)MyDataReader[1];
UpdateStock(StockID);
lock ((object) PStruct)
{
PStruct.ProgressStock = "正在更新" + StockName + "(" + StockID + ")";
PStruct.ProgressSum++;
}
UpdateAllThread.Suspend();
//if (StockID == "000007")
// break;
}
改动为
while (MyDataReader.Read())
{
StockID = (string)MyDataReader[0];
StockName = (string)MyDataReader[1];
UpdateStock(StockID);
//lock ((object) PStruct)
//{
// PStruct.ProgressStock = "正在更新" + StockName + "(" + StockID + ")";
// PStruct.ProgressSum++;
//}
Invoke_CallProgress myICP = new Invoke_CallProgress(CallProgress);
PStruct.ProgressSum++;
BeginInvoke(myICP,"正在更新" + StockName + "(" + StockID + ")",PStruct.ProgressSum); UpdateAllThread.Suspend();
//if (StockID == "000007")
// break;
}
最后
lz在“全部股票更新ToolStripMenuItem_Click”方法中的
while (PStruct.ProgressSum <= TotalStockNum)
{
AllUD.Refresh();
if(UpdateAllThread.ThreadState == ThreadState.Suspended||UpdateAllThread.ThreadState == ThreadState.SuspendRequested)
{
lock ((object)PStruct)
{ AllUD.progressBar1.Value = PStruct.ProgressSum;
AllUD.label1.Text = PStruct.ProgressStock;
}
//AllUD.Refresh();
UpdateAllThread.Resume();
MyDataGridView.Refresh();
menuStrip1.Refresh();
}
if(UpdateAllThread.ThreadState == ThreadState.Stopped)
break;
//Thread.Sleep(10);
} 这段代码就可以注释掉了,在主进程中开启while循环来刷新控件的显示,及时是加上了sleep也会使cpu占用率很高。<以上代码未经测试,是直接写在回复框中的,lz可参考思路,自行测试>补充建议:
1、在UpdateProgress()方法中,若要调用窗体控件,一定要使用Invoke委托来调用。
2、建议lz的代码分分层:数据库操作的部分用一个类、业务代码独立为一个单独的方法、界面的按钮事件仅调用独立的业务方法。否则,如果将来需求有所改动,你的工作量可大了去了。