Open a window in a thread

If I understood well, the app runs in the main thread, if some methods take long, the apple OS shows a spinning color wheel. I tried to show a window with a simple text like “Wait, processing…” and a counter.

But this do not work because when I open the window which it creates a thread I’m opening the window in the main thread, no? And the window is not updated until the long process is over.
Probably I should open a new thread and this opens the WaitwWindow but I could not find an example or a way to implement this.

And suggestions?

One of the ways to do this is to have your thread initiated from the wait window using a Timer. Then your window will open, the Timer will fire 50ms later which starts the thread.

This is how I implemented it, the thread is started in the window open
Sub Open() Handles Open
myThread.Start
End Sub
But this does not work, the window open as blank window and keeps this state until the procedures that take long have finished.

It won’t work, especially if you’re not sleeping the thread during your code within that thread. When you call myThread.Start in Open, the Open event won’t complete until either the Thread sleeps or completes its work. That’s why I suggested using a Timer with its Mode set to Off and Interval set to 50. Then, at the very end of your Open event, set the Timer’s Mode to Single.

Why?

That is how it works.

In the open just use Xojo.Core.Timer.CallLater to call a sub and put the myThread.Start in that sub

From the Documentation:

How CPU Time is Shared Between Threads

Threads are managed by the built-in Thread Scheduler. Its task is to allocate the CPU’s processing cycles among all of the application’s threads. Note that because Threads are cooperative, they all share the CPU cycles of a single CPU core. Threads cannot access other CPU cores. The Priority property determines how many or how few processing cycles the Thread gets, relative to the main thread (which always has a priority of 5) and any other Threads that you have created. Use a higher value for Priority to give a thread more CPU cycles and a lower value to give it less CPU cycles.

Threads run in the background, but are temporarily blocked by certain user actions:

  • While the mouse button is held down,
  • While a window is being dragged,
  • While a menu in the menu bar is pulled down,
  • While the mouse button is pressed on a control in a window (not true of Windows)

When the Thread Scheduler decides to stop execution of the current thread and allow another thread to run, it is called a context switch. The amount of time a thread runs is called the time slice for the thread.

Threads can yield time to other threads and other applications each time they execute a looping construct such as For, While, and Do. However, a thread does not necessarily yield time at every loop boundary even though it has an opportunity to do so. A thread actually yields to another thread when the Thread Scheduler decides that its timeslice has expired. Context switches are expensive, so the Thread Scheduler always tries to avoid them.

You can also force context switches by calling Application.YieldToNextThread or by calling Application.SleepCurrentThread.

Without the thread yielding time to other threads, the context switch may not ever happen.

I would have expected that calling start() merely queues that thread for execution, and that the Open then continues to completion. At which point the scheduler looks for threads or events requiring CPU time and the newly started thread gets some CPU time according to the various priorities.

That would likely cause unexpected behaviors as compared to the way other operations in the Xojo language happen. I, personally, expect something to start when I tell it to start, rather than at some random time later.

I don’t see anything in the doc page you referenced to say one way or the other. My expectation is based on prior experience of writing thread/task based systems. I would find it strange if the Open were blocked in the way you describe.

That’s why you have the YieldToNextThread and SleepCurrentThread methods. They give you the control to say “OK, I want this to run, but let’s take care of other things first.”

Testing in the latest version of Xojo, however, I don’t see the lockup that used to be indicative of using the Thread class in this manner. Likely due to the addition of UserInterfaceUpdate incorporating a Timer internally to check for messages that need to be sent. So something else is probably going on here.

Something I’m doing wrong because it does not work. (the App.YieldToNextThread is in the run)

Sub Run() Handles Run
  While True
    Count = Count + 1
    App.YieldToNextThread
  Wend
End Sub

What does the rest of your Open event look like? What about the Open events of the controls on the window?

Yes I know that and I use them in my threads. If a thread gets priority immediately, then the implication is that one might do worse than to put a yield in as the first statement of any CPU-bound thread.

The log in my app shows 5 threads all starting at once (within the same second). These are all started in a loop in a timer event, but my threads all do lots of I/O and not much processing, so I can’t tell from that whether the timer event is held up until a thread happens to wait for I/O.

My assumption has always been, however, that starting a thread just queues it, rather than switching to it immediately. I’ve not seen anything to contradict that.

I made a simple example where a button starts everything

    Sub Action() Handles Action
  
  WindowWait.StatusLabel.Value = "Wait, processing..."
  WindowWait.show
  
  myLoop
  WindowWait.close
End Sub

I’ll post the example binary file.

I know this wasn’t previously true, as these sorts of workaround that I mentioned were created through trial and error on this exact pain point.

https://www.dropbox.com/s/3srd0bgsq2fxpve/myThread.xojo_binary_project.zip?dl=1

It locks up because you’re calling myLoop, which is a function with a tight loop that will run on the main thread.