ExitThread() command
ExitThread() command
We all know we're not supposed to use KillThread() to "stop" a running thread, but I have times when I need to cleanly exit a thread before its normal exit - like a virtual ProcedureReturn command that the thread can receive at any time from another part of its parent app. Could a command be added to do that? Thanks.
Note 1: I'm aware that exiting a thread cleanly requires cleaning up, like closing any files it opened, etc. My app only does calculations, so it should be safe to exit like this - and besides, an ExitThread() command could be smart enough to know what's been opened/created anyway and just close/free whatever automatically when "ExitThread()" is received.
Note 2: No, I can't do this with a global "exit" variable for the thread because my app creates LOTS of threads on-the-fly (not at app startup) with lots of actions that the user has specified during runtime, so there's literally (a) no way for the thread to know what the global "exit" var for itself it would be, and (b) no way to put a check between each action of that thread for that global var because the thread doesn't know how many actions it will be executing.
So if a command like this is feasible and easy to add, it would be great!
Note 1: I'm aware that exiting a thread cleanly requires cleaning up, like closing any files it opened, etc. My app only does calculations, so it should be safe to exit like this - and besides, an ExitThread() command could be smart enough to know what's been opened/created anyway and just close/free whatever automatically when "ExitThread()" is received.
Note 2: No, I can't do this with a global "exit" variable for the thread because my app creates LOTS of threads on-the-fly (not at app startup) with lots of actions that the user has specified during runtime, so there's literally (a) no way for the thread to know what the global "exit" var for itself it would be, and (b) no way to put a check between each action of that thread for that global var because the thread doesn't know how many actions it will be executing.
So if a command like this is feasible and easy to add, it would be great!
Re: ExitThread() command
If you don't need to "clean up" ressources like files, images, memories, ... then you can use KillThread(). Edit: Of cause, the "normal way" with an exit value has to be preferred.BarryG wrote:My app only does calculations, so it should be safe to exit like this
No this is not possible. For example, some times you create images in a thread, draw somethink on it and push it to the main thread for using. Here, the ExitThread()-command should not free these images, which were created in the thread. On the other hand, some times you create images in a thread just temporary. Here, the ExitThread()-command have to free such an image. But how should ExitThread()-Command decide, which way is right? It is the task of the programer, to do all necessary clean ups.BarryG wrote:and besides, an ExitThread() command could be smart enough to know what's been opened/created anyway and just close/free whatever automatically when "ExitThread()" is received.
I did not understand your arguments. If you define a global variable "exit", then every thread can read this value and can stop its calculations or loops. Further, if you want to quit a thread with your "ExitThread()" procedure, you have to know the ThreadID, which have to stored somewhere. At the same place you also store typical objects like Mutex, Semaphores and so on, used in this thread, and also a private "exit" value. After you set this exit value, you have to use WaitThread() for all threads, to wait until they are done.BarryG wrote:Note 2: No, I can't do this with a global "exit" variable for the thread because my app creates LOTS of threads on-the-fly (not at app startup) with lots of actions that the user has specified during runtime, so there's literally (a) no way for the thread to know what the global "exit" var for itself it would be, and (b) no way to put a check between each action of that thread for that global var because the thread doesn't know how many actions it will be executing.
Btw: Also a "ProcedureReturn" is not in general a "cleanly exit". You have to free images, files, memories by yourself, otherwise you have even for procedures strong memory leaks.
PB 6.01 ― Win 10, 21H2 ― Ryzen 9 3900X, 32 GB ― NVIDIA GeForce RTX 3080 ― Vivaldi 6.0 ― www.unionbytes.de
Lizard - Script language for symbolic calculations and more ― Typeface - Sprite-based font include/module
Lizard - Script language for symbolic calculations and more ― Typeface - Sprite-based font include/module
Re: ExitThread() command
Imagine you are working in your garden.
Then another helper comes along.
You can now both work in the garden, you can also use the same tool.
But you can't use the hedge trimmer at the same time.
You can also say, I'll use the hedge trimmer, you won't get it today.
When the helper is done, he cleans up and goes home.
If he doesn't clean up, there's trouble.
A peculiarity of these helpers is that they are very hard-working, but also very stupid.
And they are never smarter than their employer.
Also, they always have to be paid.
A useless or wrongly employed helper can do more harm than good.
Then another helper comes along.
You can now both work in the garden, you can also use the same tool.
But you can't use the hedge trimmer at the same time.
You can also say, I'll use the hedge trimmer, you won't get it today.
When the helper is done, he cleans up and goes home.
If he doesn't clean up, there's trouble.
A peculiarity of these helpers is that they are very hard-working, but also very stupid.
And they are never smarter than their employer.
Also, they always have to be paid.
A useless or wrongly employed helper can do more harm than good.
地球上の平和
Re: ExitThread() command
Here's a (rough) example of what I mean. I have a lot of on-the-fly calcs that are done with different on-the-fly threads like this:
That's one type. Another thread created on-the-fly (totally different calcs and code, but shown similar here for the example) might look like this:
Now, assume the delay is 1000 ms for each thread, due to the time it takes for each calc line to finish. As you can see, both threads would take 10 secs to complete.
But, assume I want to cancel the second thread (CalcThread2) for some reason (it happens). Using a global "cancel" var that I set to "1" (to cancel), my code now has to look like this to accommodate the cancel:
See how disgusting that is? Because I need to be able to cancel at any part of the thread, so I need to If/EndIf after every command in the thread in case "cancel2=1". But there could be >100 commands in each thread! I can't code that for every thread.
Now, what about CalcThread1? What if I need to cancel that, too? I need to set a new "cancel" var just for it, so it doesn't clash with the cancel var of CalcThread2. After all, I want CalcThread2 to keep running but stop CalcThread1. So a new global var is needed, but I don't know how many threads I'm going to have, so how will I know in advance how many global "cancel" vars I will need? See the problem? I can have the following, but it's not future-proof and there's no way of any given thread to know which global var is "theirs":
It's annoying. Currently I'm dealing with it just by using KillThread(), which 100% works and does the job but I know this isn't "clean", so I was hoping for an "ExitThread" that would simulate the "If cancel : ProcedureReturn : EndIf" bits because they're literally impossible to add manually.
This is my dream solution:
Code: Select all
Procedure CalcThread1(params)
a+1 : Delay(delay_due_to_time_of_calc)
a+1 : Delay(delay_due_to_time_of_calc)
a+1 : Delay(delay_due_to_time_of_calc)
a+1 : Delay(delay_due_to_time_of_calc)
a+1 : Delay(delay_due_to_time_of_calc)
a+1 : Delay(delay_due_to_time_of_calc)
a+1 : Delay(delay_due_to_time_of_calc)
a+1 : Delay(delay_due_to_time_of_calc)
a+1 : Delay(delay_due_to_time_of_calc)
a+1 : Delay(delay_due_to_time_of_calc)
EndProcedure
Code: Select all
Procedure CalcThread2(params)
a-1 : Delay(delay_due_to_time_of_calc)
a-1 : Delay(delay_due_to_time_of_calc)
a-1 : Delay(delay_due_to_time_of_calc)
a-1 : Delay(delay_due_to_time_of_calc)
a-1 : Delay(delay_due_to_time_of_calc)
a-1 : Delay(delay_due_to_time_of_calc)
a-1 : Delay(delay_due_to_time_of_calc)
a-1 : Delay(delay_due_to_time_of_calc)
a-1 : Delay(delay_due_to_time_of_calc)
a-1 : Delay(delay_due_to_time_of_calc)
EndProcedure
But, assume I want to cancel the second thread (CalcThread2) for some reason (it happens). Using a global "cancel" var that I set to "1" (to cancel), my code now has to look like this to accommodate the cancel:
Code: Select all
Global cancel2
Procedure CalcThread2(params)
a-1 : Delay(delay_due_to_time_of_calc)
If cancel2 : ProcedureReturn : EndIf
a-1 : Delay(delay_due_to_time_of_calc)
If cancel2 : ProcedureReturn : EndIf
a-1 : Delay(delay_due_to_time_of_calc)
If cancel2 : ProcedureReturn : EndIf
a-1 : Delay(delay_due_to_time_of_calc)
If cancel2 : ProcedureReturn : EndIf
a-1 : Delay(delay_due_to_time_of_calc)
If cancel2 : ProcedureReturn : EndIf
a-1 : Delay(delay_due_to_time_of_calc)
If cancel2 : ProcedureReturn : EndIf
a-1 : Delay(delay_due_to_time_of_calc)
If cancel2 : ProcedureReturn : EndIf
a-1 : Delay(delay_due_to_time_of_calc)
If cancel2 : ProcedureReturn : EndIf
a-1 : Delay(delay_due_to_time_of_calc)
If cancel2 : ProcedureReturn : EndIf
a-1 : Delay(delay_due_to_time_of_calc)
EndProcedure
Now, what about CalcThread1? What if I need to cancel that, too? I need to set a new "cancel" var just for it, so it doesn't clash with the cancel var of CalcThread2. After all, I want CalcThread2 to keep running but stop CalcThread1. So a new global var is needed, but I don't know how many threads I'm going to have, so how will I know in advance how many global "cancel" vars I will need? See the problem? I can have the following, but it's not future-proof and there's no way of any given thread to know which global var is "theirs":
Code: Select all
Global cancel1, cancel2, cancel3, ...up to what? we don't know!
This is my dream solution:
Code: Select all
Procedure CalcThread1(params)
a+1 : Delay(delay_due_to_time_of_calc)
a+1 : Delay(delay_due_to_time_of_calc)
a+1 : Delay(delay_due_to_time_of_calc)
a+1 : Delay(delay_due_to_time_of_calc)
a+1 : Delay(delay_due_to_time_of_calc)
a+1 : Delay(delay_due_to_time_of_calc)
a+1 : Delay(delay_due_to_time_of_calc)
a+1 : Delay(delay_due_to_time_of_calc)
a+1 : Delay(delay_due_to_time_of_calc)
a+1 : Delay(delay_due_to_time_of_calc)
EndProcedure
id1=CreateThread(@CalcThread1(),0)
ExitThread(id1) ; Done later when needed, instead of KillThread(), and it just exits the procedure at the next command (ie. the next calc in my example).
- NicTheQuick
- Addict
- Posts: 1226
- Joined: Sun Jun 22, 2003 7:43 pm
- Location: Germany, Saarbrücken
- Contact:
Re: ExitThread() command
If you do it right, it is not a big issue to create threads dynamically and have a "quit" variable for everyone of them. You even can get the return value of your thread if you want to:
But the issue with terminating the thread after every calculation step still persists.
Code: Select all
EnableExplicit
Prototype.i ThreadFunc(*parameter)
Structure ThreadInfo
handle.i
quit.i
running.i
function.ThreadFunc
returnValue.i
*parameter
EndStructure
Procedure ThreadFunc(*t.ThreadInfo)
*t\running = #True
*t\returnValue = *t\function(*t)
*t\running = #False
EndProcedure
Procedure.i StartThread(function.ThreadFunc, *parameter)
Protected *t.ThreadInfo = AllocateStructure(ThreadInfo)
*t\quit = #False
*t\parameter = *parameter
*t\function = function
*t\handle = CreateThread(@ThreadFunc(), *t)
ProcedureReturn *t
EndProcedure
Procedure QuitThread(*t.ThreadInfo)
*t\quit = #True
EndProcedure
Procedure.i JoinThread(*t.ThreadInfo)
*t\quit = #True
While IsThread(*t\handle)
WaitThread(*t\handle)
Wend
ProcedureReturn *t\returnValue
EndProcedure
;--------------------------------
Procedure.i CalcThread1(*t.ThreadInfo)
Protected a
While Not *t\quit
a+1
Delay(100)
Wend
ProcedureReturn a
EndProcedure
Procedure.i CalcThread2(*t.ThreadInfo)
Protected a
While Not *t\quit
a+1
Delay(150)
Wend
ProcedureReturn a
EndProcedure
Define thread1 = StartThread(@CalcThread1(), 123)
Define thread2 = StartThread(@CalcThread2(), 456)
Delay(5000)
Debug JoinThread(thread1)
Debug JoinThread(thread2)
Last edited by NicTheQuick on Wed Jan 06, 2021 2:19 pm, edited 1 time in total.
The english grammar is freeware, you can use it freely - But it's not Open Source, i.e. you can not change it or publish it in altered way.
Re: ExitThread() command
Hi Nic, I'm a bit drunk right now but that looks more confusing than just throwing the hammer of KillThread() at it... I will look again tomorrow when sober (if I remember).
- NicTheQuick
- Addict
- Posts: 1226
- Joined: Sun Jun 22, 2003 7:43 pm
- Location: Germany, Saarbrücken
- Contact:
Re: ExitThread() command
You only have to focus on the stuff beyond this lineBarryG wrote:Hi Nic, I'm a bit drunk right now but that looks more confusing than just throwing the hammer of KillThread() at it... I will look again tomorrow when sober (if I remember).
Code: Select all
;--------------------------------
The english grammar is freeware, you can use it freely - But it's not Open Source, i.e. you can not change it or publish it in altered way.
Re: ExitThread() command
But... see this:
You've got two hard-coded procedures to handle it. But I don't know how many threads I'll have... could be 1, could be 100, could be 1000. I can't code 1000 x "Procedure.i CalcThread1(*t.ThreadInfo)" for all scenarios. It's hard for me to explain. Just know that my app can create X amount of threads on-the-fly (when the user requests one), and I need to kill any of them when the user requests it.
Code: Select all
Procedure.i CalcThread1(*t.ThreadInfo)
Re: ExitThread() command
Write and read accesses from different threads to common variables must be protected with mutex
For remember my solution for threads ...
Mini Thread Control
For remember my solution for threads ...
Mini Thread Control
My Projects ThreadToGUI / OOP-BaseClass / EventDesigner V3
PB v3.30 / v5.75 - OS Mac Mini OSX 10.xx - VM Window Pro / Linux Ubuntu
Downloads on my Webspace / OneDrive
PB v3.30 / v5.75 - OS Mac Mini OSX 10.xx - VM Window Pro / Linux Ubuntu
Downloads on my Webspace / OneDrive
- NicTheQuick
- Addict
- Posts: 1226
- Joined: Sun Jun 22, 2003 7:43 pm
- Location: Germany, Saarbrücken
- Contact:
Re: ExitThread() command
Now I don't know what you talking about. If you need fundamentally different thread procedures you indeed have to write them.
If you think you need 100 or 1000 different procedures then you are thinking wrong.
Also you can not write procedures dynamically, except you have an runtime-assembler and you are allowed to execute code from memory. But that's a whole other level.
If you just want to run the same procedure 100x times concurently, then just do it.
Here's an example for 25 concurrent threads which all do the same thing.
If you think you need 100 or 1000 different procedures then you are thinking wrong.
Also you can not write procedures dynamically, except you have an runtime-assembler and you are allowed to execute code from memory. But that's a whole other level.
If you just want to run the same procedure 100x times concurently, then just do it.
Here's an example for 25 concurrent threads which all do the same thing.
Code: Select all
Procedure.i CalcThread(*t.ThreadInfo)
Protected a
While Not *t\quit
a+1
Delay(100)
Wend
ProcedureReturn a
EndProcedure
Dim *threads(24)
Define i
For i = 0 To 24
*threads(i) = StartThread(@CalcThread(), i)
Next
Delay(5000)
For i = 0 To 24
Debug JoinThread(*threads(i))
Next
The english grammar is freeware, you can use it freely - But it's not Open Source, i.e. you can not change it or publish it in altered way.
Re: ExitThread() command
This is the problem, which I said at the start. I don't know how many threads are going to be running at any given time. What do I hard-code here? You said 24, should I put 9999 to cover all possibilities?NicTheQuick wrote:Dim *threads(24)
Anyway, let's forget workarounds and hope an ExitThread() command can be done as an alternative to KillThread(). That's all I really need.
Re: ExitThread() command
ExitThread(id) is not enough. There must also be a way to query the ExitThread status in the thread procedure in order to delete any resources before the thread is terminated. Otherwise you have the same as with KillThread.
So you can also programme it yourself. See Mini Thread Control
So you can also programme it yourself. See Mini Thread Control
My Projects ThreadToGUI / OOP-BaseClass / EventDesigner V3
PB v3.30 / v5.75 - OS Mac Mini OSX 10.xx - VM Window Pro / Linux Ubuntu
Downloads on my Webspace / OneDrive
PB v3.30 / v5.75 - OS Mac Mini OSX 10.xx - VM Window Pro / Linux Ubuntu
Downloads on my Webspace / OneDrive
- NicTheQuick
- Addict
- Posts: 1226
- Joined: Sun Jun 22, 2003 7:43 pm
- Location: Germany, Saarbrücken
- Contact:
Re: ExitThread() command
Well, then use a LinkedList. That was just an example. I assumed you know how to use Arrays, LinkedLists and such things.BarryG wrote:This is the problem, which I said at the start. I don't know how many threads are going to be running at any given time. What do I hard-code here? You said 24, should I put 9999 to cover all possibilities?NicTheQuick wrote:Dim *threads(24)
Anyway, let's forget workarounds and hope an ExitThread() command can be done as an alternative to KillThread(). That's all I really need.
But keep in mind that you can not run infinitely many threads. Every system and operating system has some limits.
Maybe you should consider using thread pools. For this you create a bunch of threads at the start of your program that wait for tasks and then feed them their tasks from an other thread.
The english grammar is freeware, you can use it freely - But it's not Open Source, i.e. you can not change it or publish it in altered way.
Re: ExitThread() command
As already said: your 'program flow' is wrong.
You should re- think about your current thread solution.
But (@NicTheQuick) a thread has no return value.
The result has to be placed in the parameter struct or it needs PostEvent().
You should re- think about your current thread solution.
But (@NicTheQuick) a thread has no return value.
The result has to be placed in the parameter struct or it needs PostEvent().
- NicTheQuick
- Addict
- Posts: 1226
- Joined: Sun Jun 22, 2003 7:43 pm
- Location: Germany, Saarbrücken
- Contact:
Re: ExitThread() command
I created my example in a way so that a thread can have a return value. This is also quite common in other programming languages. There you usually have a function like "join" which waits until the thread terminates and returns the return value of that thread.infratec wrote:As already said: your program flow is wrong.
But (@NicTheQuick) a thread has no return value.
The result has to be placed in the parameter struct or it needs PostEvent().
I used the structure ThreadInfo in my example above.
The english grammar is freeware, you can use it freely - But it's not Open Source, i.e. you can not change it or publish it in altered way.