【问题标题】:Correct usage of BackgroundWorker in WPFWPF中BackgroundWorker的正确使用
【发布时间】:2023-03-27 05:39:01
【问题描述】:



我正在尽力掌握 C# 和 WPF 中的线程,尤其是在 WPF 中。

问题的背景:
我有一个 MVVM 应用程序,它的身份验证过程很长(几秒钟长)。我想让 UI 负责(保持应用程序窗口可拖动和调整大小,以及显示带有 gif 元素的预加载器)。 为了实现这一点 - 我已经分离了所有 UI 元素和数据库检索。之后 - 我在登录视图中实现了 A 后台工作人员。

这里是登录视图的代码:

    private void ValidateLogin()
    {
        Usuario user = new Usuario();
        MainWindow mw = Window.GetWindow(this) as MainWindow;
        mw.preloaderShow();

        BackgroundWorker bw = new BackgroundWorker();

        bw.DoWork += (o, args) =>
        {   
            user = _viewmodel.Login(tbLogin.Text, tbPassword.Password); //TimeConsuming DataRetrieval from DB
        };
        bw.RunWorkerCompleted += (o, args) =>
        {   
            if (user != null)
            {
                if (user.Activo == 0)
                {
                    mw.preloaderHide();
                    CustomMessageBox WrongLoginMessage = new CustomMessageBox("El usuario esta inactivo.");
                    WrongLoginMessage.ShowDialog();
                }
                else
                {
                    AppSession.Instance.SetValue("currentuser", user);
                    btnProceed.Visibility = Visibility.Visible;
                    mw.preloaderHide();
                }
            }
        };

        bw.RunWorkerAsync();
    }

问题:
显然,我在 bw.DoWork 上陷入了死锁,因为“调用线程无法访问此对象,因为不同的线程拥有它。”。

问题:
1. 由于我没有更新 bw.DoWork() 中的任何 UI(此时让我们将其视为事实), - 为什么后台工作人员很忙?我的意思是 - 据我了解,引入整个概念是为了在分离的线程中以尽可能少的痛苦运行进程?
2. 如何在保留 UI 的同时从数据库中检索用户反应灵敏?也许 BackgroundWorker 不是实现这个目标的最佳概念(Task / TaskFactory)?

如果能帮助我解决这个问题,我将不胜感激。 提前谢谢你。

【问题讨论】:

  • 我可以简单地猜测下投票的原因。
  • 在 WPF 解决方案中你真的应该避免像BackgroundWorker 这样又老又重的对象
  • @VMAtm 我能理解你想说什么,但如果你能说出你推荐什么解决方案(任务?)会更有用。
  • 为什么需要处理专用线程? async 操作在线程池中完成,您不控制线程。而BackgroundWorker 是重物,您应该测试这两种方法以查看速度差异。
  • stephencleary.com - 从async 开始阅读的好地方。祝你的项目好运

标签: c# wpf multithreading backgroundworker


【解决方案1】:

简单的经验法则是您不能从后台线程访问 UI 元素。您正在访问 DoWork 中的文本框和密码控件,这会导致异常。

在 UI 线程上获取用户名和密码,你很好。

string userName = tbLogin.Text;
string password = tbPassword.Password;
bw.DoWork += (o, args) =>
{   
   user = _viewmodel.Login(userName, password); 
};

【讨论】:

  • 我的朋友 - 这就是解决方案。我所学到的——你不仅不能更新 UI,还不能与 BackgrounWorker 的 UI 交互。谢谢。
【解决方案2】:
private async void ValidateLogin()
{
    MainWindow mw = Window.GetWindow(this) as MainWindow;
    mw.preloaderShow();

    Task<Usuario> taskLogin = Login(tbLogin.Text, tbPassword.Password);
    await taskLogin;

    Usuario user = taskLogin.Result;

    if (user != null)
    {
        if (user.Activo == 0)
        {
            mw.preloaderHide();
            CustomMessageBox WrongLoginMessage = new CustomMessageBox("El usuario esta inactivo.");
            WrongLoginMessage.ShowDialog();
        }
        else
        {
            AppSession.Instance.SetValue("currentuser", user);
            btnProceed.Visibility = Visibility.Visible;
            mw.preloaderHide();
        }
    }
}

public Task<Usuario> Login(string loginText, string password)
{
    return Task.Run(() =>
    {
        return new Usuario();
    });
}

使用任务而不是后台工作者。

【讨论】:

  • 使用 Task 的显着好处是什么? (我是认真的 - 它工作得更快/更轻)
  • 更多信息here.
  • 我不喜欢你编写后台工作者(语法)的方式。当您使用任务时,代码看起来更清晰。使用任务没有很大的优势。它使用更新的 API 并更好地处理异步程序。我不知道它是否更快。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-23
  • 2011-06-14
相关资源
最近更新 更多