飞溅,穿线和竞赛条件

时间:2016-08-25 15:07:35

标签: c# wpf multithreading mvvm

不确定这是最好的标题。

我的线程遇到了一些问题。在Application_Startup中,我启动了一个新线程,在静态助手类的帮助下打开了一个简单的启动画面窗口。在Application_Startup中,有一种检查用户是否已登录的方法,如果他尚未登录,则会打开登录窗口。此时,如果出现登录窗口,则启动画面应为close,但它不会。

    private void Application_Startup(object sender, StartupEventArgs e)
    {
        Thread thread = new Thread(
        new ThreadStart(
            delegate ()
            { 
                SplashScreenHelper.SplashScreen = new Splash()
                // this works. the splash screen opens
                SplashScreenHelper.Show();
                System.Windows.Threading.Dispatcher.Run();
            }
        ));
        thread.SetApartmentState(ApartmentState.STA);
        thread.IsBackground = true;
        thread.Start();

        check();
    }


    private void check()
    {  
        var clientid = (int) IPdevices.Properties.Settings.Default["clientid"];
        if (clientid > 0)
        {
            try
            {
                // this works
                SplashScreenHelper.ShowText("Looking for profile...");
            }
            catch (EntityException)
            {
            }
        }
        else
        {
            // This does not work
            SplashScreenHelper.Close();
            // this opens up fine.
            openLoginWindow();
        }
    }

当我们在else方法中遇到check条件时,事情开始失败。首先,我在SplashScreenHelper.Close();中设置了一个断点,发现Splash的实例为空,因此来自Close窗口的Splash从未触发(请参阅下面的课程)。但令人感到好奇的是,如果它被正确实例化并显示启动画面窗口,它怎么能为null?

这是SplashScreenHelper课程,以获得一个想法:

class SplashScreenHelper
{
        public static Splash SplashScreen { get; set; }

        public static void Show()
        {
            if (SplashScreen != null)
            {
                SplashScreen.Show();
            }    
        }

        public static void Close()
        {
            if (SplashScreen == null) return;
            if (!SplashScreen.Dispatcher.CheckAccess())
            {
                SplashScreen.Dispatcher.Invoke(DispatcherPriority.Normal,
                    new Action(delegate()
                    {
                        System.Windows.Threading.Dispatcher.CurrentDispatcher.InvokeShutdown();
                        SplashScreen.Close();
                    }));
            }
            else
            {
                SplashScreen.Close();
            }
        }

        public static void ShowText(string text)
        {
            if (SplashScreen == null) return;

            if (!SplashScreen.Dispatcher.CheckAccess())
            {
                SplashScreen.Dispatcher.Invoke(
                    DispatcherPriority.Normal,

                    new Action(delegate ()
                    {
((SplashScreenViewModel)SplashScreen.DataContext).SplashScreenText = text;
                    }
                ));
            }
            else
            {
                ((SplashScreenViewModel)SplashScreen.DataContext).SplashScreenText = text;
            }

        }
    }

当我们点击else时,我有两个窗口:启动画面窗口和登录窗口,虽然启动窗口应该已经关闭但是它没有,因为启动画面实例是{{ 1}}这是我无法弄清楚会发生什么的地方。

我认为发生的事情是,null方法以某种方式在线程启动之前触发,打开登录窗口,点击else语句,尝试关闭启动画面但是启动画面线程从未启动过并且从未实例化它。 然后,然后线程最终实例化启动屏幕。

1 个答案:

答案 0 :(得分:0)

在调用check之前,您需要确保第二个线程中代码的第一部分运行。

ManualResetEvent对此非常有用!

using (ManualResetEvent mre = new ManualResetEvent(false))
{
    Thread thread = new Thread(new ThreadStart(
        delegate ()
        { 
            SplashScreenHelper.SplashScreen = new Splash()
            // this works. the splash screen opens
            SplashScreenHelper.Show();
            mre.Set(); // signal that we are done setting up the splash screen
            System.Windows.Threading.Dispatcher.Run();
        }
    ));
    thread.SetApartmentState(ApartmentState.STA);
    thread.IsBackground = true;
    thread.Start();

    mre.WaitOne(); // wait until the signal is received
}

check(); // now we can safely call check()