[英]Why when I use Thread class in Console Application, my Application waits until it ends?
[英]When I use cross thread then my application doesn't respond until background operations complete
我正在從.net Framework 2.0中的應用程序開發Windows。
在后台運行一些操作,例如數據庫備份,進度條和標簽文本更新等。但是當我使用跨線程時,我的應用程序直到后台操作完成后才響應(忙圖標)
這是示例代碼
private void button1_Click(object sender, EventArgs e)
{
Thread t = new Thread(new ThreadStart(UpdateInfo));
t.Start();
}
private void UpdateInfo()
{
if (this.InvokeRequired)
{
this.Invoke(new MethodInvoker(UpdateInfo));
}
else
{
// send query to database here for taking backup that could take time
// update progress bar
//I'm also using sqlconnection InfoMessage here
label1.Text = "Text upading......
}
}
private void OnInfoMessage(sender As Object, e As SqlInfoMessageEventArgs)
{
}
方案:方案是用戶可以取消操作,但由於應用程序未響應而無法取消操作
===============更新代碼=============================== ==========
我的代碼就像
private void btnBackup_Click(object sender, EventArgs e)
{
Thread t = new Thread(new ThreadStart(MyThreadFunc));
t.Start();
}
public void MyThreadFunc()
{
if (this.InvokeRequired) {
this.Invoke(new MethodInvoker(Backup));
} else {
Backup();
}
}
public void Backup()
{
string databaseName = cbDatabase.Text;// getting the name of database for backup
SaveFileDialog1.ShowDialog(); // dialog will open
string backupFileName = SaveFileDialog1.FileName; // getting location of backup
//============ database query==================
SqlConnection con = new SqlConnection(conString);
con.FireInfoMessageEventOnUserErrors = true;
con.InfoMessage += OnInfoMessage;
con.Open();
query = string.Format("backup database {0} to disk = {1}", databaseName,backupFileName);
using (cmd == new SqlCommand(query, con)) {
cmd.CommandTimeout = 0;
cmd.ExecuteNonQuery();
}
con.Close();
con.InfoMessage -= OnInfoMessage;
con.FireInfoMessageEventOnUserErrors = false;
//============ Database operation end==================
}
private void OnInfoMessage(object sender, SqlInfoMessageEventArgs e)
{
lblStatusMsg.Text = e.Message; // mostly messages are like. 1 percent complete, 5 percent complete, 11 percent complete
foreach (SqlError info in e.Errors) {
if (info.Class > 10) {
// errror logging
} else {
Regex reger = new Regex("\\d+");
Match regerMatch = reger.Match(e.Message);
if (ProgressBar1.Value == 100) {
} else {
ProgressBar1.Value = regerMatch.Value;
}
}
}
}
在數據庫操作完成之前不響應問題
Invoke
的目的是使代碼在主線程上運行。 因此,您的代碼正在創建一個線程,其全部目的是強制主線程運行所有代碼。
假設您要運行一個線程,該線程在啟動后10秒鍾將更新標簽文本以指示完成。 您仍然需要Invoke
標簽更新,但這是唯一應在調用中進行的操作。
在這種情況下,您的線程函數應如下所示:
private void MyThreadFunc()
{
// do something here
Thread.Sleep(10000);
// update the label:
if (label1.InvokeRequired)
Invoke(UpdateLabel);
else
UpdateLabel();
}
private void UpdateLabel()
{
label1.Text = "Something was finished.";
}
換句話說,您需要分離出那些必須在主線程上運行的東西(例如更新窗體上控件的任何東西),然后僅Invoke
那些位。 其余的應該在Invoke
之外進行。
我想我沒有說清楚。
Invoke
方法用於在擁有您要調用的控件或窗體的句柄的線程的上下文中執行代碼。 您可以使用它與UI上的控件進行交互,但是您僅應將其用於此目的。 如果將所有線程的關閉都放在Invoke
調用中,則所有線程的代碼都將在UI線程中運行,這使得擁有一個單獨的線程變得毫無意義。
如果您想在事情發生時停止應用程序的UI暫停(畢竟這是使用線程的主要原因之一),那么僅應在絕對必要時使用Invoke
方法,然后再對其中很小的部分使用Invoke
方法碼。 調用Invoke
來更新控件的參數,與表單的非線程安全屬性進行交互等。您可以直接從其他線程使用對話框等,盡管有些人也喜歡使用Invoke
。
而且,如果您要進行多次調用,則可能應該編寫一些輔助方法來包裝Invoke
來清理內容。 就像是:
public void Invoker(Action action)
{
if (InvokeRequired)
Invoke(action);
else
action();
}
public T Invoker<T>(Func<T> func)
{
if (InvokeRequired)
return (T)Invoke(func);
else
return func();
}
現在,您可以以最小的影響編寫線程代碼,如下所示:
public void ThreadFunc()
{
System.Threading.Thread.Sleep(1000);
Invoker(() => this.label1.Text = "Started");
for (int i = 1; i < 10; i++)
{
System.Threading.Thread.Sleep(1000);
Invoker(() => this.label1.Text = string.Format("Iteration {0}", i));
}
System.Threading.Thread.Sleep(1000);
Invoker(() => this.label1.Text = "Completed");
}
或者,如果您不喜歡lambda函數(由於某種原因),則可以使用如下方法:
public void Invoker<T>(Action<T> action, T p)
{
if (InvokeRequired)
Invoke(action, p);
else
action(p);
}
private void SetLabel(string value)
{
label1.Text = value;
}
然后在您的代碼中:
Invoker(SetLabel, "new text value");
重要的部分是使所調用的代碼很小,否則最終將阻塞主線程。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.