【问题标题】:UI Thread is Blocked when showing Splash Screen [duplicate]显示启动画面时 UI 线程被阻止 [重复]
【发布时间】:2012-08-02 05:14:39
【问题描述】:

可能重复:
C# Splash Screen Problem

我是 c# 的新手,我正在处理软件启动时运行的启动屏幕。我在启动屏幕类中有一个检查数据库的函数。我正在使用线程来调用函数

        sc = new splashScreen();

        checkDLLThread = new Thread(new ThreadStart(sc.checkDLLS).BeginInvoke);
        checkDLLThread.Start();

        while (checkDLLThread.IsAlive)
        {
            Thread.Sleep(200);
        }

问题是 UI 被阻塞,直到线程处于活动状态。最后它给了我数据库连接状态消息。 这是我的代码。我使用了 checkDLLThread.join() 但它也不起作用。

【问题讨论】:

  • 你为什么不直接删除睡眠?这就是阻止 UI 的原因。
  • 我已经尝试删除 sleep ,然后循环阻塞 UI
  • 好吧,当然也要删除循环;-) 解除对 UI 线程的阻塞需要从事件处理程序返回。
  • 这就是问题所在,我想检查数据库是否已初始化,它不仅仅是一个闪屏,我正在后台检查我的数据库。如果数据库连接失败,则会显示正确的消息并退出应用程序
  • 如果可以的话,这个答案中有一些有用的例子:stackoverflow.com/questions/48916/…。这里还有codeproject.com/Articles/5454/A-Pretty-Good-Splash-Screen-in-C 的例子;您可以在主线程继续其需要完成的工作时在新线程中显示启动屏幕,并且在完成后,它可以向启动屏幕发出信号以关闭。

标签: c# database winforms thread-safety


【解决方案1】:

启动画面只是在您的应用加载时“逗乐”用户的图像。使用 app_load 方法在启动时执行代码:

像这样:(在 app.xaml 和 app.xaml.cs 中)

    <Application /some references to stuff/ Startup="Application_Startup" >

    private void Application_Startup(object sender, StartupEventArgs e)
    {
        // your startupcode
    }

另外,如果您不想打扰 UI,我认为 BackGroundworker 类更适合这样的事情。

【讨论】:

  • 我用的是VS 2005,在哪里可以找到这个东西
  • 我假设您正在使用 WPF。您使用的是 Windows 窗体吗?
  • 我认为将代码放入 Application_Startup 也会阻塞(为什么不呢?想不出任何理由)。
  • 是的,我正在使用 Windows 窗体
  • 如果它仍然阻塞 UI,试试我上面链接的 BackGroundworker 类。
【解决方案2】:

解除对 UI 线程的阻塞需要从事件处理程序返回。除了这样做,别无选择。这是一些伪代码:

OnStartup:
 Start new Thread
 Disable UI
 Show Splash Sceen
 Return!

Thread:
 Check Database
 if (not ok)
  Show Message box
  Exit Application
 else
  Enable UI
  Remove Splash Screen

诀窍是让线程显示消息,然后在完成后显示消息。不要等待线程,让线程自己做。

您的线程可能需要使用 Invoke 函数来访问 UI。您可能不想阅读这方面的内容,因为如果您想在后台线程上执行异步工作,则无法绕过它。

【讨论】:

    【解决方案3】:

    以下代码在您的应用程序(在我下面的示例中称为 MainForm())加载或初始化时在单独的线程上启动“启动屏幕”。首先在您的“main()”方法中(在您的 program.cs 文件中,除非您已重命名它)您应该显示您的启动画面。这将是您希望在启动时向用户显示的 WinForm 或 WPF 表单。这是从 main() 启动的,如下所示:

    [STAThread]
    static void Main()
    {
        // Splash screen, which is terminated in MainForm.       
        SplashForm.ShowSplash();
    
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
    
        // Run UserCost.
        Application.Run(new MainForm());
    }
    

    在您的 SplashScreen 代码中,您需要以下内容:

    public partial class SplashForm : Form
    {
    
        // Thredding.
        private static Thread _splashThread;
        private static SplashForm _splashForm;    
    
        public SplashForm()
        {
            InitializeComponent();
        }
    
        // Show the Splash Screen (Loading...)      
        public static void ShowSplash()    
        {        
            if (_splashThread == null)        
            {            
                // show the form in a new thread            
                _splashThread = new Thread(new ThreadStart(DoShowSplash));            
                _splashThread.IsBackground = true;            
                _splashThread.Start();        
            }    
        }    
    
        // Called by the thread    
        private static void DoShowSplash()    
        {        
            if (_splashForm == null)            
                _splashForm = new SplashForm();        
            // create a new message pump on this thread (started from ShowSplash)        
            Application.Run(_splashForm);
        }    
    
        // Close the splash (Loading...) screen    
        public static void CloseSplash()    
        {        
            // Need to call on the thread that launched this splash        
            if (_splashForm.InvokeRequired)            
                _splashForm.Invoke(new MethodInvoker(CloseSplash));        
            else            
                Application.ExitThread();    
        }
    
    }
    

    这会在单独的后台线程上启动启动表单,让您可以同时继续渲染主应用程序。要显示有关加载的消息,您必须从主 UI 线程中提取信息,或者以纯粹的审美性质工作。要在应用程序初始化后完成并关闭启动画面,请将以下内容放在默认构造函数中(如果需要,可以重载构造函数):

        public MainForm()
        {
            // ready to go, now initialise main and close the splashForm. 
            InitializeComponent();
            SplashForm.CloseSplash();
        }
    

    上面的代码应该比较容易理解。

    我希望这会有所帮助。

    【讨论】:

      【解决方案4】:

      您的方法很好,但您应该同时运行这两种方法。善用static fields 可以轻松完成这项工作。而不是:

          while (checkDLLThread.IsAlive)
          {
              Thread.Sleep(200);
          }
      

      你应该:

      • 显示启动画面Form
      • 设置表单在启动时隐藏,即将Opacity设置为0
      • 当您的表单完全初始化时,稳定地检查SplashForm 中的变量
      • 当线程完成时将Opacity 设置回1。即变量变化

      i.e.:

      public Form1(){
           InitializeComponent();
           Opacity = 0;
           while(sc.IsComplete){}
           Opacity = 1;
      }
      

      在你的SplashForm 里面,你应该有类似的东西

      internal static bool IsComplete;
      
      internal static void CheckDLLS()
      {
          //after doing some stuff
          IsComplete = true;
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-03-04
        • 1970-01-01
        • 1970-01-01
        • 2012-12-09
        • 1970-01-01
        相关资源
        最近更新 更多