Page 1 of 1
Posted: Tue Oct 29, 2002 9:25 pm
by BackupUser
Restored from previous forum. Originally posted by cwhite.
Hi all!
I've done a fair bit of programming with threads in C/C++ and Java. I've started playing with threads in PureBasic, and have several general questions:
1. When accessing a shared resource (i.e. a global variable), does PureBasic automatically synchronize access from multiple threads to the resource? For example, if I have two threads incrementing a single global variable, will PureBasic ensure that the first thread fully finishes incrementing the variable before the second thread can change it?
2. In Win32 programming, Microsoft recommends that all UI updates occur in the main window thread, not from within worker threads. Is this the recommended method in PureBasic? I ask, because I have seen sample code posted where the UI is being updated from worker (not the main UI) threads.
Thanks much!
-Chris
Registered PureBasic Programmer
Posted: Wed Oct 30, 2002 6:16 am
by BackupUser
Restored from previous forum. Originally posted by fweil.
Hello Chris,
More or less I first agree that you should follow MS recommandation about UI when using threads, but as it is called a recommandation, you also have many possibilities to make updates from procedures, which is not necessarily bad. BTW you may consider callbacks as an alternative.
Second point, your first question, you will find here a small sample code that confirms that global variables are well processed even from multiple threads. As a thread corresponds to a block of instructions executed until the stack can be used by another thread or another program section, no way the flow can be processed such as a variable would not be 'completely processed' inside a thread. Well the theory is not as easy to explain when using multiple processors, but it is the same.
Just look at this code hereafter and let me know if it helps you to understand and helps.
Code: Select all
Global i.l, j.l
Procedure inc1()
Repeat
i + 1
Delay(1000)
ForEver
EndProcedure
Procedure inc2()
Repeat
i + 1
j + 1
Delay(1000)
ForEver
EndProcedure
Procedure inc3()
Repeat
j + 1
Delay(1000)
ForEver
EndProcedure
;
;
;
Quit.l = #FALSE
If OpenWindow(0, 200, 200, 320, 200, 0, "")
AddKeyboardShortcut(0, #PB_Shortcut_Escape, 99)
If CreateGadgetList(WindowID())
TextGadget(100, 10, 10, 80, 20, "")
TextGadget(110, 10, 35, 80, 20, "")
EndIf
CreateThread(@inc1(), 0)
CreateThread(@inc2(), 0)
CreateThread(@inc3(), 0)
Repeat
Select WindowEvent()
Case #PB_EventCloseWindow
Quit = #TRUE
Case #PB_EventMenu
Select EventMenuID()
Case 99
Quit = #TRUE
EndSelect
EndSelect
SetGadgetText(100, Str(i))
SetGadgetText(110, Str(j))
Delay(10)
Until Quit
EndIf
End
Francois Weil
14, rue Douer
F64100 Bayonne
Posted: Wed Oct 30, 2002 6:51 am
by BackupUser
Restored from previous forum. Originally posted by Danilo.
> As a thread corresponds to a block of instructions executed until
> the stack can be used by another thread or another program section,
> no way the flow can be processed such as a variable would not
> be 'completely processed' inside a thread.
Nope, thats wrong for the multitasking and multithreading OS Windows
(i think this was true in Win3.1 because it had cooperative multitasking).
If this would still be true, you could lock Windows by making a thread
that runs forever.
But Windows is switching to another thread and doesnt care what
you did at this time. All the current modes and stack pointer
etc.. are saved and restored on task switching.
But what means "ensure that the first thread fully finishes
incrementing the variable before the second thread can change it" ??
You cant set the calling order of the threads, so how do you know
which thread is the first one ?? Creation order ??
What when you change the thread priority ?? I dont think you
can control the calling order directly.
And incrementing a variable is only 1 ASM opcode, so it cant be
breaked, IMHO.
cya,
...Danilo
(registered PureBasic user)
Posted: Wed Oct 30, 2002 7:33 am
by BackupUser
Restored from previous forum. Originally posted by fweil.
Yes Danilo the sample code does not show where a thread breaks because it is a snigle increment. I just wanted to show how a global variable may be changed by several threads without trouble.
By the way, depending on what you are processing, it can become much complicate to understand how multiple threads will affect data synchronization, especially when using threads that update records on files for example. Chris's question was just about global variables.
The threading concept is not to consider the whole "thread's code" as a block of instructions, but only part of it after which the operrating system can access the stack to switch. I am not skilled enough to tell you which part, but I know it is so. This is the reason why the operating system will not lock in a forever loop. This is also the reason why you will never have a good way to know the threads order after a certain time ... because the operating system is not real time, ticks based, with lots of interruptions making threads waiting for variable time.
This also the reason it is recommended to "orchestrate" threads otherwise you could have amazing behaviour of your apps.
So it is a hard and long discussion about synchronous / asynchronous programming !
KRgrds
Francois Weil
14, rue Douer
F64100 Bayonne
Posted: Wed Oct 30, 2002 8:43 am
by BackupUser
Restored from previous forum. Originally posted by freak.
> 2. In Win32 programming, Microsoft recommends that all UI updates occur in the main window
> thread, not from within worker threads. Is this the recommended method in PureBasic? I ask,
> because I have seen sample code posted where the UI is being updated from worker (not the main
> UI) threads.
Every UI stuff needs to be updated from within the thread, where it was created from. That's becourse every thread has its own message queue, and so UI messages are only send to the
thread that created the Window/Control/... .
So you can create a new thread, with it's own Window and Stuff, but then you'll need to
process it's messages there.
Hope this helps a bit...
Timo
Posted: Wed Oct 30, 2002 11:28 am
by BackupUser
Restored from previous forum. Originally posted by Sweenie.
[Edit] Oops, I saw that fweil already mentioned this [Edit]
Hi.
How about the command UseFile?
I have two threads both reading it's own file.
The first thread reads from file 0 and
the second thread reads from file 1.
To read from file 0 , thread 1 needs to use the command
Usefile(0) before reading from the file.
What if thread 2 performs the command UseFile(1) right after thread 1 has performed UseFile(0)?
Won't both threads read from File 1?
While Eof(0)=0
UseFile(0)
<---------- What if another thread performs a UseFile(1) here?
ReadData(*Pointer,4096)
Wend
I hope you understand what I mean.
Regards, Sweenie
Posted: Wed Oct 30, 2002 12:34 pm
by BackupUser
Restored from previous forum. Originally posted by fweil.
Sweenie,
I understand what you mean and I recommend you to use some technics like messaging to manage files well. Depending on what you do, you will have better if possible to centralize R/W operations in a specific procedure that will manage files operations otherwise it is possible you get bad results.
By messaging technic, I mean to send a file number and a string or data block to read or write so that threads will not mix-up in your logic.
But I repeat it depends on what you exactly want to do. It is just my recommendation.
Francois Weil
14, rue Douer
F64100 Bayonne
Posted: Wed Oct 30, 2002 1:32 pm
by BackupUser
Restored from previous forum. Originally posted by Sweenie.
fweil: I see what you mean.
But if the filenr to use had been part of the ReadData command
would that had solved the problem, or would the same problem
still be able to happen "inside" the readdata command on a lower level.
example:
ReadData(FileID#,*Pointer,4096)
instead of
UseFile(FileID#)
ReadData(*Pointer,4096)
//Sweenie
Posted: Wed Oct 30, 2002 2:14 pm
by BackupUser
Restored from previous forum. Originally posted by fweil.
...,
I suppose, but I am not sure testing a larger part of code you have or intent to have, that it may occur the threaded procedures will not be synchronized with the main program or other parts of code reading Data blocks. So my opinion is that it exist many risks of issues ...
Rgrds
Francois Weil
14, rue Douer
F64100 Bayonne
Posted: Wed Oct 30, 2002 2:59 pm
by BackupUser
Restored from previous forum. Originally posted by cwhite.
I have found what appears to be a significant bug in the Thread package. I have written a test application to demonstrate the issue. Description of problem:
A message posted from a worker thread to the main UI thread will be ignored or discarded when the left mouse-button is pressed and held down while the cursor is over the titlebar (for example, if you're moving the window with the mouse).
I wrote a similar test application with C, and did not encounter this problem. Therefore, either there is a bug in PureBasic, a bug in my PureBasic code, or I am trying to do something that PureBasic can't handle. Thoughts or ideas? Here is source code for a complete test application that demonstrates the problem:
Code: Select all
;- Constants
#WM_APP = $8000
#CW_SEARCH_COMPLETED = #WM_APP + 1
;- Global variables
Global hMainWnd.l
Global mainThreadId.l
;- Procedure declarations
Declare.l CountFiles(directory.s)
; Create a window for display in center of desktop
mainWidth = 300
mainHeight = 300
flags = #PB_Window_WindowCentered | #PB_Window_SystemMenu | #PB_Window_SizeGadget
hMainWnd = OpenWindow(0, 0, 0, mainHeight, mainWidth, flags, "Finder")
If CreateGadgetList(WindowID())
top = 10
left = 10
gadgetHeight = 21
gadgetWidth = 120
TextGadget(1, left, top+4, 45, gadgetHeight, "Filename:") : left + 53
StringGadget(2, left, top, 175, gadgetHeight, "")
top + 28
ButtonGadget(3, left, top, 40, gadgetHeight, "&Find", #PB_Button_Default)
top + 28
ListViewGadget(4, left, top, 175, 100)
AddGadgetItem(4, -1, "File One")
AddGadgetItem(4, -1, "File Two")
EndIf
;Add keyboard shortcuts
AddKeyboardShortcut(0, #PB_Shortcut_Return, 1001)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;Main event loop
Repeat
eventID = WaitWindowEvent()
;Debug eventID
If eventID = #CW_SEARCH_COMPLETED
;search completed
Debug "Received #CW_SEARCH_COMPLETED message in main thread!"
AddGadgetItem(4, -1, "#CW_SEARCH_COMPLETED - message received")
MessageRequester("Info", "SEARCH_COMPLETED", 0)
ElseIf eventID = #PB_EventGadget ;a gadget has been pushed
Select EventGadgetID()
Case 3
;Find button was pressed - count files in a worker thread
tid = CreateThread(@CountFiles(), "C:\winnt\system32")
Debug "New thread - " + Str(tid)
EndSelect
EndIf
Until eventID = #PB_EventCloseWindow
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; CountFiles - count the files in a specified directory
;; This runs in it's own thread
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
Procedure.l CountFiles(directory.s)
Delay(3000) ; delay 3 seconds to simulate some activity on worker thread
PostOK = 0
While PostOK = 0
If PostMessage_(hMainWnd, #CW_SEARCH_COMPLETED, 0, 0) = 0
;failed - unable to post message to main thread - wait a second and try
;again
Debug "Post failed. Trying again in one second..."
Delay(1000)
Else
Debug "Post succeeded."
PostOK = 1
EndIf
Wend
ProcedureReturn 0
EndProcedure
Registered PureBasic Programmer - 3.40
Posted: Wed Oct 30, 2002 5:17 pm
by BackupUser
Restored from previous forum. Originally posted by Sweenie.
Would the following code work?
The functions should of course be supplied with criticalsection object.
At the startup:
InitializeCriticalSectionAndSpinCount_()
In each thread:
...
EnterCriticalSection_()
While Eof(FileID#)=0
UseFile(FileID#)
ReadData(*Pointer,4096)
Wend
LeaveCriticalSection_()
...
At shutdown of application:
DeleteCriticalSection()
If i understood it right the EnterCriticalSection function will make sure that the thread waits for it's turn to execute the code following the EnterCriticalSection function.
//Sweenie
Posted: Wed Oct 30, 2002 5:18 pm
by BackupUser
Restored from previous forum. Originally posted by SoulTaker.
See the Windows API Functions on Event Objects...
Abit BD7-II Raid P4 1.9 gHz 384 Ram Xtasy GFroce 3 TI 200 CD-RW DirectX 9.0 Beta 3 Sound Blaster Live! XP Pro, Registered PureBasic User Version 3.40 For Windows.
Posted: Thu Oct 31, 2002 6:29 am
by BackupUser
Restored from previous forum. Originally posted by Danilo.
Originally posted by Sweenie
fweil: I see what you mean.
But if the filenr to use had been part of the ReadData command
would that had solved the problem, or would the same problem
still be able to happen "inside" the readdata command on a lower level.
example:
ReadData(FileID#,*Pointer,4096)
instead of
UseFile(FileID#)
ReadData(*Pointer,4096)
With "ReadData(FileID#,*Pointer,4096)"
it wouldnt be a problem.
But its not only FileAccess... there are
also other UseXXX commands like UseImage() etc..
Fred cant change it anymore, because he had to
change half of the commands of PB.
Looks like PB wasnt written with Threads in mind
and the threads library was an addon later.
One solution for you would be to use API
for this.
Generally, its not a good idea to use many
files at the same time because the HDD-header
always jumps from 1 position to the other.
And this is very time consuming, means slow.
cya,
...Danilo
(registered PureBasic user)