I have run into an issue that is making me frustrated.
I’m running Xojo 2025R2 on mac Sonoma.
I have a thread that will update on a window a label, so people can see the program is running, even if it isn’t showing anything.
here is the code for threadForRunningTime.Run
static runTimerValue As Integer
While app.runningTime = TRUE
runTimerValue = runTimerValue + 1
Me.AddUserInterfaceUpdate("timeCounter":runTimerValue)
Thread.SleepCurrent(1000)
Wend
And in my threadForRunningTime.UserInterfaceUpdate:
Var seconds As Integer
If data.LastIndex >= 0 Then
Var arg As Dictionary = data(data.LastIndex)
If arg.HasKey("timeCounter") Then
seconds = arg.Value("timeCounter").IntegerValue
labelProgramRunTime.Text = seconds.ToString
End If
End If
The issue I have is that instead of getting 1..2..3.. it screams up to tens of thousands. And just stops.
Or locks up my program and xojo.
If I remove the Me.AddUserInterface… it seems to act normally according to my log file.
7:12:19 PM : There are 45 total rows 7:12:21 PM : ******** MATCH FOUND ******** : I = 4 J = 6 : ******** MATCH FOUND ******** : I = 4 J = 18 : ******** MATCH FOUND ******** : I = 4 J = 20 7:12:22 PM : ******** MATCH FOUND ******** : I = 6 J = 18 : ******** MATCH FOUND ******** : I = 6 J = 20 7:12:23 PM : ******** MATCH FOUND ******** : I = 9 J = 16 : ******** MATCH FOUND ******** : I = 9 J = 19 : ******** MATCH FOUND ******** : I = 9 J = 23 7:12:24 PM : ******** MATCH FOUND ******** : I = 11 J = 12 7:12:25 PM : ******** MATCH FOUND ******** : I = 13 J = 37 : ******** MATCH FOUND ******** : I = 14 J = 22 7:12:26 PM : ******** MATCH FOUND ******** : I = 15 J = 43 : ******** MATCH FOUND ******** : I = 16 J = 19 : ******** MATCH FOUND ******** : I = 16 J = 23 : ******** MATCH FOUND ******** : I = 17 J = 21 : ******** MATCH FOUND ******** : I = 18 J = 20 7:12:27 PM : ******** MATCH FOUND ******** : I = 19 J = 23 7:12:28 PM : ******** MATCH FOUND ******** : I = 24 J = 31 7:12:29 PM : ******** MATCH FOUND ******** : I = 26 J = 27 : ******** MATCH FOUND ******** : I = 28 J = 33 7:12:32 PM : Done and Dusted : ABOUT TO STOP RUNNING TIME : RUNNING TIME IS NOW: False 7:1
The reason I am using a thread as a timer is I have the program doing processor heavy work. If I try to do it with a timer, my program doesn’t update until after the work has been completed.
First thing I would try is Me.Sleep(1000) instead of Thread.SleepCurrent(1000) since you have a reference to the thread instance. There is no good reason to call the shared method version in this case.
But I agree with the others, your approach sounds backwards. Threads should be used for the heavy work so the main thread can keep the UI responsive. Doing the heavy work in the main thread and trying to use a thread as a timer isn’t really going to work since you still need the main thread to actually show the progress.
That’s where I would start my refactoring. I do not recommend preemptive threads, at least not at this stage. They’re a fantastic way to shave a few years off your life expectancy due to stress. Cooperative threads will take you a long way in most workloads. And if not, come on back and we’ll help you with workers and/or preemptive threads.
A question however. How do i set the priority of the main thread.
When I create the other threads, I can set their priority. And set them as pre-emptive or cooperative.
Now a quick update (?)
I created a very simple project 1 button, 1 label, 1 thread, 1 timer and it works perfectly with this code:
In the button.Press
app.runningTime = TRUE
Window1.Thread1.Start
System.DebugLog "Button pressed"
// for testing only
System.DebugLog "The thread has been started"
In the thread.run
static runTimerValue as Integer
while app.runningTime = TRUE
runTimerValue = runTimerValue + 1
Me.AddUserInterfaceUpdate("timeCounter":runTimerValue)
Thread.SleepCurrent(1000)
wend
In the thread.UserInterfaceUpdate:
Var seconds As Integer
For Each arg As Dictionary In data
If arg.HasKey("timeCounter") Then
seconds = arg.Value("timeCounter")
Label1.Text = seconds.ToString
End If
next
In my timer.run
Thread1.Resume
And it works perfectly.
As a note / notes:
I have to use a static variable in thread.run, or the value of the variable resets every time the thread runs.
And the reason I use the threads is because BEFORE I did this in my real program, the UI for the progam did indeed become unresponsive (or gave the illusion). The program ran, finished cleanly, but I hated the fact that I couldn’t do anything with the window.
explained when the ui updates do his job (without a thread)
means if you are inside of a method the ui update do not work,
it will called after your method is finished
this main event loop is hidden and handled by xojo framework.
Anyway, I can explain the process and the behavior to myself.
do
ui update
dosomething
loop
function dosomething
do something longer
end function
with a thread it is different
in its run event it can do something as long as you want because its interrupted by the xojo framework to do something else and update the UI, and later the thread continue.
that is the reason you put this dosomething longer method in a thread and start it there.
The main thread is locked to priority 5. What is your worker thread doing? Cooperative threads yield at loop boundaries, so if you’re making a plugin or other call that does not itself yield, a separate thread will not help.
Your code sounds more complicated than it needs to be though. On your window you should have a button, a timer, a thread, and a property. The button will start the timer, thread, and clear the counter. The thread does its work. The property stores the counter. The timer increments the counter and updates the label. Use a UserInterfaceUpdate at the end of the thread to signal when the work is complete so you can update the UI accordingly, such as stopping the timer. That’s really all there is to it. If your UI locks up when built this way, it means your work thread isn’t yielding any time to other threads. That’s the true problem that needs to be solved.
Without knowing what the work thread is doing, it’s impossible to suggest a solution. It could need some explicit yield or sleep statements, it could need to be a worker, or maybe even a preemptive thread.
Thanks to everyone who helped. I found the problem, though I have NO idea why this worked.
In my threadForRunningTimer, all I had to do was change Pre-emptive to Collaborative.
If anyone can explain why that worked (and I know in one of the many test iteratives of the program I created I did do that) I’m willing to be educated.
I still hope you’ve not disregarded the advice you got in this thread. Your main thread should do the minimal work and a secondary thread should be where most of the work is done.
I have the impression you have tried to solve your problem while continuing in the same wrong way, and that’s not going to work in the end. Always better to refactor earlier rather than when all seems done and you’re finally forced to refactor way more than you could have done.
This is because a cooperative thread yields to other threads (including the main one) when it can, like between iterations of a loop. I wonder, though, whether you saw the difference when debugging in the IDE; last I knew, all threads are cooperative in the IDE, and you only see the difference in a built app.
Respectfully, though, per my earlier comment and in agreement with Arnaud, it sure sounds like your approach is backwards, and will give you maintenance problems in the future.