应用程序停止在新线程函数中工作
application stop working in new thread function
当我使用消息框来跟踪程序停止的位置时
在 com.CommandText
它什么都不显示,应用程序挂起(冻结)。
如果我在没有 Thread
.
的情况下使用它,同样的功能将完美运行
这是什么原因?
public bool load_complete=false;
private void button7_Click(object sender, EventArgs e)
{
Thread t1 = new Thread(new ThreadStart(load_data));
t1.Start();
Thread.Sleep(2000);
while (load_complete == false) ;
t1.Abort();
}
public void load_data()
{
string connectionString = @"Data Source=(LocalDB)\v11.0;AttachDbFilename=" + Application.StartupPath + "\Database1.mdf;Integrated Security=True;";
SqlConnection conn = new SqlConnection(connectionString);
conn.Open();
MessageBox.Show("open");
SqlCommand com = new SqlCommand();
try
{
com.Connection = conn;
DataSet ds = new DataSet();
DataTable dt = new DataTable();
SqlDataAdapter da = new SqlDataAdapter();
com.CommandText = "select username,password from login where username ='" + textBox1.Text + "'";
da.SelectCommand = com;
da.Fill(dt);
dataGridView1.DataSource = dt;
username = (string)this.dataGridView1.Rows[0].Cells[0].Value;
pass = (string)this.dataGridView1.Rows[0].Cells[1].Value;
load_complete = true;
}
catch (Exception ex){
MessageBox.Show(ex.Message);
}
}
简短版本:您正在阻塞 UI 处理消息框显示的线程,不要阻塞 UI 线程
更长的版本:
上面列出的代码存在许多问题。
让我们从显而易见的开始:
1.
public bool load_complete=false;
...
while (load_complete == false) ;
您的 flag
未标记为 volatile 并且编译器优化可能假定由单个线程访问,在这种情况下您的代码将永远停留在 UI 中。如果不是因为代码中的许多其他问题,我建议在这里使用 volatile...
2.
Thread.Sleep(2000);
您正在阻塞 UI 线程,因此任何用户交互时间超过 2 秒,这是 CS101 及以后的大忌,请考虑在此处使用任务和 async/await 以保持 UI 不间断。对仍然使用 SqlDataAdapter 的代码进行的最小更改需要:
a) 将按钮处理程序标记为异步并等待您需要的任何内容:
private async void button7_Click(object sender, EventArgs e)
{
await load_data();
// more UI code is free to go here
}
b) 运行 load_data
在线程池上异步并返回一个 Task 对象给调用者:
public Task load_data()
{
return Task.Run( () =>
{
// paste your old load_data code here verbatim
});
}
3.
t1.Abort();
无法想象那个线程对你做了什么,所以你想冷血地打断它。线程到达你传入的函数的末尾就会终止,这里绝对不需要手动中止,如果下次碰巧需要等待线程,请考虑Join方法[=19] =]
另一种选择是使用 BackgroundWorker,正如它所说,它使用不会占用主 UI 线程的后台线程。它已经带有流程完成时的事件。
您只需将 BackgroundWorker 从工具箱拖到您的窗体中。
注意:在您的 LoadData 方法中,您无法访问任何表单元素,因此您必须将 MessageBox 取出。您可以在 BackgroundWorker 完成的事件处理程序中引用表单元素。
private void button7_Click(object sender, EventArgs e)
{
MyWorker.RunWorkerAsync();
}
private void MyWorker_DoWork(object sender, DoWorkEventArgs e)
{
LoadData();
}
private void MyWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Error == null)
{
string username = (string)this.dataGridView1.Rows[0].Cells[0].Value;
string pass = (string)this.dataGridView1.Rows[0].Cells[1].Value;
}
else
{
MessageBox.Show("Houston we have a problem");
}
}
private void LoadData()
{
// Load your datagridview
}
当我使用消息框来跟踪程序停止的位置时
在 com.CommandText
它什么都不显示,应用程序挂起(冻结)。
如果我在没有 Thread
.
这是什么原因?
public bool load_complete=false;
private void button7_Click(object sender, EventArgs e)
{
Thread t1 = new Thread(new ThreadStart(load_data));
t1.Start();
Thread.Sleep(2000);
while (load_complete == false) ;
t1.Abort();
}
public void load_data()
{
string connectionString = @"Data Source=(LocalDB)\v11.0;AttachDbFilename=" + Application.StartupPath + "\Database1.mdf;Integrated Security=True;";
SqlConnection conn = new SqlConnection(connectionString);
conn.Open();
MessageBox.Show("open");
SqlCommand com = new SqlCommand();
try
{
com.Connection = conn;
DataSet ds = new DataSet();
DataTable dt = new DataTable();
SqlDataAdapter da = new SqlDataAdapter();
com.CommandText = "select username,password from login where username ='" + textBox1.Text + "'";
da.SelectCommand = com;
da.Fill(dt);
dataGridView1.DataSource = dt;
username = (string)this.dataGridView1.Rows[0].Cells[0].Value;
pass = (string)this.dataGridView1.Rows[0].Cells[1].Value;
load_complete = true;
}
catch (Exception ex){
MessageBox.Show(ex.Message);
}
}
简短版本:您正在阻塞 UI 处理消息框显示的线程,不要阻塞 UI 线程
更长的版本:
上面列出的代码存在许多问题。 让我们从显而易见的开始:
1.
public bool load_complete=false;
...
while (load_complete == false) ;
您的 flag
未标记为 volatile 并且编译器优化可能假定由单个线程访问,在这种情况下您的代码将永远停留在 UI 中。如果不是因为代码中的许多其他问题,我建议在这里使用 volatile...
2.
Thread.Sleep(2000);
您正在阻塞 UI 线程,因此任何用户交互时间超过 2 秒,这是 CS101 及以后的大忌,请考虑在此处使用任务和 async/await 以保持 UI 不间断。对仍然使用 SqlDataAdapter 的代码进行的最小更改需要:
a) 将按钮处理程序标记为异步并等待您需要的任何内容:
private async void button7_Click(object sender, EventArgs e)
{
await load_data();
// more UI code is free to go here
}
b) 运行 load_data
在线程池上异步并返回一个 Task 对象给调用者:
public Task load_data()
{
return Task.Run( () =>
{
// paste your old load_data code here verbatim
});
}
3.
t1.Abort();
无法想象那个线程对你做了什么,所以你想冷血地打断它。线程到达你传入的函数的末尾就会终止,这里绝对不需要手动中止,如果下次碰巧需要等待线程,请考虑Join方法[=19] =]
另一种选择是使用 BackgroundWorker,正如它所说,它使用不会占用主 UI 线程的后台线程。它已经带有流程完成时的事件。
您只需将 BackgroundWorker 从工具箱拖到您的窗体中。
注意:在您的 LoadData 方法中,您无法访问任何表单元素,因此您必须将 MessageBox 取出。您可以在 BackgroundWorker 完成的事件处理程序中引用表单元素。
private void button7_Click(object sender, EventArgs e)
{
MyWorker.RunWorkerAsync();
}
private void MyWorker_DoWork(object sender, DoWorkEventArgs e)
{
LoadData();
}
private void MyWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Error == null)
{
string username = (string)this.dataGridView1.Rows[0].Cells[0].Value;
string pass = (string)this.dataGridView1.Rows[0].Cells[1].Value;
}
else
{
MessageBox.Show("Houston we have a problem");
}
}
private void LoadData()
{
// Load your datagridview
}