【问题标题】:C# WPF UI Freeze Issue MultiThreadingC# WPF UI 冻结问题多线程
【发布时间】:2016-10-12 20:39:37
【问题描述】:

我有如下代码,我面临 UI Hung 问题,没有直接的方法来重现它。它随机发生 10 次。我无法确定导致问题的原因。

我有下面的按钮点击代码,它将显示一个子窗口:-

private void processCart_Click(object sender, RoutedEventArgs e)
{
    // processcart is Child Window Like Dialog To Open on Top Of Main Window
    processcart = new ProcessCart();

    processcart.Closed += ProcessCart_OnClosed;

    // Disable Parent Window 
    this.Dispatcher.Invoke(new Action(() =>
    {
        try
        {
            this.IsEnabled = false;
        }
        catch (Exception ex)
        { }
    }));

    processcart.Owner = Window.GetWindow(this);

    // Openning Child Window
    processcart.Show();
}

private void ProcessCart_OnClosed(object sender, EventArgs e)
{
    // On Child Window Close Doing some UI Update on Main Window
    totalChangeAmount.Dispatcher.Invoke(new Action(() =>
    {
        try
        {
            totalChangeAmount.Text = XFuelMath.roundingTwo(cart.ChangeTotal) + "";
        }
        catch
        { }
    }));

    // Starting Background Worker Thread for Database operation.
    if (!bgwTransaction.IsBusy)
    {
        bgwTransaction.RunWorkerAsync(cart);
    }
}

private void bgwTransaction_DoWork(object sender, DoWorkEventArgs e)
{
    // Doing some database operation but no UI Operation 

    // I dont know if i can call task inside background worker. 
    Task.Run(() => DeleteCurrentTicketDataFromDB(dataList, false)); 

    // Calling another background worker inside it 
    if (!bgwPrintRecipt.IsBusy)
    {
        bgwPrintRecipt.RunWorkerAsync();
    }
}

private void bgwPrintRecipt_DoWork(object sender, DoWorkEventArgs e)
{
    // Sending Data to printer 
}

private void bgwPrintRecipt_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    // No Ui Operation Here 
}

private void bgwTransaction_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    // Some UI Operation 
    totalSurcharge.Dispatcher.Invoke(() =>
    {
        totalSurcharge.Text = "0";
    });

    saleNumber.Dispatcher.Invoke(() => { });

    saleDataGrid.Dispatcher.Invoke(new Action(() =>
    {
        try
        {
            saleDataGrid.ItemsSource = currentSharedTicketCollection;
            saleDataGrid.Items.Refresh();

            if (isautoscroll && item!=null)
            {
                saleDataGrid.ScrollIntoView(item, saleDataGrid.Columns[0]);
                saleDataGrid.SelectedItem = item;
            }
        }
    }));

    // Enable the parent window Again 
    this.Dispatcher.Invoke(new Action(() =>
    {
        try
        {
            this.IsEnabled = true;
        }
        catch (Exception ex)
        { }
    }));
}

我不确定我是否在内部调用 BackgroundWorker DoWorkRunWorkerCompleted。 请指导我在此代码中需要更改什么?

【问题讨论】:

  • 我首先要摆脱所有空的 catch 块
  • 我在 catch 块中有一些日志记录代码,所以我在发布时删除了它。使代码更短。
  • 混用BackgroundWorker和Task.Run只会让人头疼,二选一吧。

标签: c# wpf backgroundworker


【解决方案1】:

您应该删除 processCart_Click 处理程序中的 Dispatcher 调用,这将在同一个 UI 线程中执行,并且根本没有必要,除非您在不同的线程中创建了多个 WPF 窗口。

此外,您应该将调试器附加到挂起的进程并查看它挂起的位置。如果您在执行它的机器上没有安装调试器,您可以右键单击任务管理器并创建一个转储文件。该文件可以加载到 Visual Studio 中。

如果您的应用程序是在 Windows x64 上执行的 32 位应用程序,您需要关闭任务管理器并从 C:\Windows\SysWOW64\Taskmgr.exe 启动该应用程序以在 64 位 Windows 上进行 32 位转储。如果您不这样做,您将无法使用托管调用堆栈将其加载到 VS 中。

【讨论】:

  • 那么在RunWorkerCompleted中使用Dispatcher对吗?或者我也不需要它
  • 如果您的工作线程在不同的线程上运行,那么您需要调度程序调用来调用 UI 线程。如果您删除它们,您将收到异常,告诉您您确实尝试从与创建控件不同的线程访问控件。
  • 我已将 exe 文件提供给用户,并且我确实包含了用于调试符号的 pdb 文件。我是否需要将此文件包含在 exe 中以使用任务管理器生成转储文件并检查挂起的问题。
  • 当子窗口关闭并且 ProcessCart_OnClosed 在父窗口中作为事件处理程序被调用时,它是另一个 UI 线程还是同一个 UI 线程的一部分?我需要专注于这个方法来处理 UI 的事情吗?
  • 不,除了挂起的用户计算机上的 .dmp 文件之外,您不需要任何东西。您还可以使用不同的转储工具,例如 SysInternals 的命令行工具 procdump.exe,它将始终创建正确位数的转储。 procdump -ma YourExe.exe
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-01-16
  • 1970-01-01
  • 2011-06-22
  • 2021-12-21
  • 1970-01-01
  • 2018-12-24
相关资源
最近更新 更多