为什么使用多线程实现登录时,Label的MouseDown事件需要点击两次才生效?
根本原因
你的代码逻辑执行顺序存在错误:
- 调用
user_login_thread.Start()后,登录线程刚启动尚未执行完User_Login逻辑,你就立刻判断if (login.UserLoggedIn),此时这个标识还是默认值false,第一次点击时跳转逻辑根本不会执行 - 后续你调用
user_login_thread.Join()阻塞了UI线程,等登录线程执行完成、UserLoggedIn被改为true时,前面的跳转判断代码已经执行完毕,不会再触发 - 第二次点击时,上一次登录操作已经把
UserLoggedIn设为true,启动线程后立刻判断就能命中条件完成跳转,这就是需要点击两次的核心原因
其他隐藏问题
- 你在非UI线程的
User_Login中调用MessageBox.Show,属于跨线程操作UI,会出现不可预期的异常或行为 - 开启多线程后调用
Join会直接卡住UI线程,完全失去了多线程避免界面卡顿的意义 - 不要使用全局数据库连接
conn,多线程环境下极易出现连接状态异常,最好每次操作数据库时新建连接、用完自动释放
修复方案
把跳转、记住密码的逻辑都移到登录完成之后执行,所有UI操作都要切回UI线程运行,参考修改代码如下:
private async void tbLogin_MouseDown(object sender, MouseButtonEventArgs e) { // 先赋值登录参数 login.Student_Email = txtstd_Email.Text; login.Student_Password = txtstd_Password.Password; // 用Task开后台线程执行登录,不会阻塞UI bool loginSuccess = await Task.Run(() => User_Login(login.Student_Email, login.Student_Password)); if (loginSuccess) { // 登录成功后再执行跳转,await会自动切回UI线程,可以直接操作UI foreach (Window window in Application.Current.Windows) { if (window is MainWindow mainWindow) { mainWindow.frmView.Source = new Uri("Views/Dashboard.xaml", UriKind.Relative); } } if (chkRemember.IsChecked == true) { Properties.Settings.Default.Student_Email = login.Student_Email; Properties.Settings.Default.Student_Password = login.Student_Password; Properties.Settings.Default.Save(); } } } // 修改User_Login返回登录结果,不要在内部直接操作UI private bool User_Login(string email, string password) { Secure security = new Secure(); // 每次操作新建数据库连接,用using自动释放 using (SqlConnection conn = new SqlConnection("你的数据库连接字符串")) { conn.Open(); string sql = "SELECT Student_Password FROM Student_Data WHERE Student_Email=@Email"; using (SqlCommand cmd = new SqlCommand(sql, conn)) { cmd.Parameters.Add("@Email", System.Data.SqlDbType.VarChar, 55).Value = email; SqlDataReader reader = cmd.ExecuteReader(); if (reader.Read() && login.Student_Password.Equals(security.Decrypt(reader["Student_Password"].ToString()))) { login.UserLoggedIn = true; return true; } else { // 切回UI线程弹出错误提示 Application.Current.Dispatcher.Invoke(() => { MessageBox.Show("Login Unsuccessful", "Student Login Unsuccessfull", MessageBoxButton.OK, MessageBoxImage.Error); }); return false; } } } }
如果课程要求必须使用Thread而不能用Task,只需要在Thread的执行逻辑末尾,通过Dispatcher把登录成功后的后续操作抛到UI线程执行即可,不要在启动线程后立刻判断登录状态。
内容的提问来源于stack exchange,提问作者PureWare -Legends Of Gaming
相关产品推荐
相关产品推荐

