Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Problems with loading screen in separate Thread

I have an old application in Windows Forms, which in many places do some searches on database. Sometimes it takes a lot of time, so I decided to create a loading screen in wpf to show the user that something is loading in separate thread. Basically it's just a full transparent window with loading indicator(a turning circle). Everything works fine on My host computer and on my Virtual Machine, but when I'm trying to deploy it to our demo environments its like - it starts loading the indicator is shown and after few seconds it dissapear and application stops responding like forever. My first thought was that it's the problem with GPU acceleration, that it can't process transparency, but it's being shown for few seconds so it can't be the problem. So most likely I did something bad. Below You can see my code, do You notice something which might be wrong/cause deadlock or something ?

  public class LoadingManager
  {
    public LoadingManager()
    { }
    public LoadingManager(string LoadingText)
    {
        loadingText = LoadingText;
    }

    private string loadingText = "Please wait ..";
    private Thread thread;
    private bool ThreadReadyToAbort = false;
    private BusyIndicatorView loadingWindow;

    public void BeginLoading()
    {
        this.thread = new Thread(this.RunThread);
        this.thread.IsBackground = true;
        this.thread.SetApartmentState(ApartmentState.STA);
        this.thread.Start();
    }

    public void EndLoading()
    {
        if (this.loadingWindow != null)
        {
            this.loadingWindow.Dispatcher.BeginInvoke(DispatcherPriority.Normal, (Action)(() =>
            { this.loadingWindow.Close(); }));
            while (!this.ThreadReadyToAbort) { }; // I also tried to remove this while but it didn't help
        }
        this.thread.Abort();
    }

    public void RunThread()
    {
        this.loadingWindow = new BusyIndicatorView();

        loadingWindow.tbLoadingCaption.Text = loadingText;
        this.loadingWindow.Closing += new System.ComponentModel.CancelEventHandler(waitingWindow_Closed);
        this.loadingWindow.ShowDialog();
    }

    void waitingWindow_Closed(object sender, System.ComponentModel.CancelEventArgs e)
    {
        Dispatcher.CurrentDispatcher.InvokeShutdown();
        this.ThreadReadyToAbort = true;
    }

EDIT.

I noticed that on this machines it usually(sometimes it also fails at first click) works when i click search for the first time. If i click another time it's showing for a second than dissapearing and application stops responding. So it seems like Thread is not beeing shutdown, Dispatcher shutdown failed ? But no exceptions are thrown ...

like image 757
MajkeloDev Avatar asked Oct 31 '22 22:10

MajkeloDev


1 Answers

Your BeginLoading method can be called more than once before it has finished, and so can create more than one wpf window. This messes up all kinds of references. Also do not abort the thread, let it decide for itself. Here are the two changes:

public void BeginLoading()
{
    this.thread = new Thread(this.RunThread);
    this.thread.IsBackground = true;
    this.thread.SetApartmentState(ApartmentState.STA);
    this.thread.Start();
    while (this.loadingWindow == null) { } // <--- Add this line
}

public void EndLoading()
{
    if (this.loadingWindow != null)
    {
        this.loadingWindow.Dispatcher.BeginInvoke(DispatcherPriority.Normal, (Action)(() =>
        { this.loadingWindow.Close(); }));
        while (!this.ThreadReadyToAbort) { }; 
    }
    //this.thread.Abort(); // <-- Remove this line
}

Also if this is all just for a busy screen, I would say there has to be a better and safer way than this. For academic purposes this is an interesting problem, but not production code, especially if some junior developer could fiddle with this in the future.

Edit: Still crashing on my machine if I reduce the delay between repeated callds to BeginLoading and EndLoading. It may be related to how the wpf window closes asynchronously. I removed the Closed event handler and just used a boolean flag to indicated that the window 'isLoaded' or not, and I have not seen any problems with this:

public class LoadingManager2
{
  public LoadingManager2()
  { }
  public LoadingManager2(string LoadingText)
  {
    loadingText = LoadingText;
  }

  private string loadingText = "Please wait ..";
  private Thread thread;
  private MyWindow loadingWindow;
  private bool isLoaded = false;

  public void BeginLoading()
  {
    this.thread = new Thread(this.RunThread);
    this.thread.IsBackground = true;
    this.thread.SetApartmentState(ApartmentState.STA);
    this.thread.Start();
    while (!this.isLoaded) { };
  }

  public void RunThread()
  {
    this.loadingWindow = new MyWindow();
    this.isLoaded = true;
    this.loadingWindow.ShowDialog();
  }

  public void EndLoading()
  {
    this.loadingWindow.Dispatcher.BeginInvoke(DispatcherPriority.Normal, (Action)(() =>
    { 
      this.loadingWindow.Close();
      this.isLoaded = false;
    }));
    while (this.isLoaded) { }; 
  }
}
like image 113
Dean Avatar answered Nov 08 '22 05:11

Dean