【问题标题】:Background thread doing DB access hangs UI执行 DB 访问的后台线程挂起 UI
【发布时间】:2011-02-03 09:59:07
【问题描述】:

我制作了一个小实用程序来测试 PC 是否有可能连接到某个 Oracle 数据库。

为了保持 UI 响应并查看进度步骤,我将 DB 代码放在后台线程中。令我惊讶的是,UI 仍然挂起(但没有那么多)。

这个app真的没什么大不了,但是我觉得这个案例总体来说很有趣,线程中的DB代码挂了UI线程!

    private void bgwDataAccess_DoWork(object sender, DoWorkEventArgs e)
    {
        BackgroundWorker bgw = sender as BackgroundWorker;

        try
        {
            bgw.ReportProgress(0, "Starting...\r\n");
            bgw.ReportProgress(0, "Active ConnectionString:\r\n");
            bgw.ReportProgress(0, Settings.Default.ConnctionString + "\r\n\r\n");

            OracleConnection con = new OracleConnection(Settings.Default.ConnctionString);
            OracleCommand cmd = new OracleCommand("SELECT Count(*) FROM MYTABLE", con);


            bgw.ReportProgress(0, "Opening db...\r\n");
            con.Open();
            bgw.ReportProgress(0, "Opened.\r\n\r\n");

            bgw.ReportProgress(0, "Executing SQL-query...\r\n");
            Object result = cmd.ExecuteScalar();
            bgw.ReportProgress(0, String.Format("Result: {0}\r\n\r\n", result.ToString()));

            con.Close();
        }
        catch (Exception)
        {
            throw;
        }
    }

    private void bgwDataAccess_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        txtResult.Text += e.UserState;           
    }

【问题讨论】:

  • 小提示,没有解决你的问题,去掉catch,在finally中无论如何都要使用try-finally关闭连接。您应该在 try 块之前声明(但不打开)连接,以便能够在 finally 中使用 con 对象。
  • 是的,你完全正确!
  • 你能定义/描述“挂起 UI 线程”吗?这有多严重?
  • 为什么你在做一个BackgroundWorker bgw = sender as BackgroundWorker;和 bgw.ReportProgress(0, "开始...\r\n");?您可以直接调用 bgwDataAccess.ReportProgress() 对吗?也使用 Using() 而不是显式关闭/处理连接
  • 您确定BackgroundWorker设置为支持报告进度(WorkerReportsProgress)?

标签: c# database multithreading oracle user-interface


【解决方案1】:

在此之前,您是否在代码中的任何位置访问过 Oracle 命名空间?这只是一个猜测,但也许暂停是您的应用程序加载所需的 .dll

您可以尝试预加载模块。我在我的应用程序中使用类似下面的代码。首先,我显示一个启动屏幕来显示应用程序正在加载,然后调用下面的 sn-p 来加载所有必需的 dll。这样一来,一旦加载应用程序,就不会再有任何暂停。

void PreloadDLLs()
{
    Assembly^ assembly = Assembly::GetEntryAssembly();
    array<System::Reflection::AssemblyName^>^ referencedAssemblies = assembly->GetReferencedAssemblies();
    for each(System::Reflection::AssemblyName^ referencedAssemblyName in referencedAssemblies)
    {
        try
        {
            Assembly^ a = assembly->Load(referencedAssemblyName);
        }
        catch(System::Exception^ /*e*/)
        {

        }
    }
}

为 C++/CLI 语法道歉,但希望您能看到如何将其转换为 C# - 我的有点生疏:-)

[编辑]我认为这几乎是 C#:

using System;
using System.Reflection;

private void PreloadDLLs()
{
    Assembly assembly = Assembly.GetEntryAssembly();
    System.Reflection.AssemblyName[] referencedAssemblies = assembly.GetReferencedAssemblies();
    foreach(System.Reflection.AssemblyName referencedAssemblyName in referencedAssemblies)
    {
        try
        {
            Assembly a = assembly.Load(referencedAssemblyName);
        }
        catch
        {

        }
    }
}

【讨论】:

  • 感谢您的回答。我试过了,但在这种情况下没有任何区别。尽管您的建议是一个不错的技巧,将来可能会有用。
【解决方案2】:

您可以将查询更改为“SELECT top 1 id FROM MYTABLE”,效果是一样的。

如果影响不是由这些操作引起的,您可以使用分析器找出哪些 .net 代码导致影响。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-10-13
    • 2019-05-08
    相关资源
    最近更新 更多