Page 1 of 1
Multithreading Demo
Posted: Sat Feb 19, 2011 10:20 am
by akj
Here is a very useful multithreading demo I converted from some code at
http://www.codeguru.com/columns/dotnet/ ... .php/c4593 .
It is simple, but clearly demonstrates the far better responsiveness that multitasking provides.
To use the program, click the button labelled "Run Nonthreaded" and then [try to] quickly click the "Test Responsiveness" button a few times. The result is very unsatisfactory.
But now repeat the test with the "Run Multithreaded" and "Test Responsiveness" buttons and you will see a dramatic difference even though the underlying code is almost identical.
The combination of "Run Multithreaded" and "Exit" buttons is also very responsive.
Code: Select all
; Multithreaded Response
; www.codeguru.com/columns/dotnet/article.php/c4593
; When either the "Run Nonthreaded" or "Run Multithreaded" buttons are pressed
; they will be temporarily disabled and a list box will be filled with
; "Hello World" messages with a delay between each.
; The window also contains a button to test the interface responsiveness while
; the list box is being populated.
; The click events of the "Run Nonthreaded" and "Run Multithreaded" buttons
; result in the same procedure being executed. The only difference is that when
; the "Run Multithreaded" button is pressed the method is executed in a new thread.
#Program$ = "Multithreaded Response"
#Version$ = "1.0"
EnableExplicit
; Constants
Enumeration
#winMain
#lblResponse
#txtResponse
#lstHello
#butNonthread
#butThreaded
#butTest
#butExit
EndEnumeration
Declare FillList(*value)
Declare ToggleButtons(toggle)
; GUI Metrics
Define gap, lstw, lsth, butw, buth, winw, winh
Define lblw, lblh, txtw, txth
gap = 20
lblw = 100: lblh = 24
txtw = 100: txth = lblh
lstw = 500: lsth = 200
butw = 150: buth = 30
winw = lstw+gap*2: winh = txth+lsth+buth*2+gap*7
; Create GUI
Define title$, flags, x, y
title$ = " "+#Program$+" "+#Version$
flags = #PB_Window_SystemMenu|#PB_Window_MinimizeGadget|#PB_Window_TitleBar|#PB_Window_ScreenCentered
OpenWindow(#winMain, 0, 0, winw, winh, title$, flags)
; Labelled text box
y = gap: x = gap
TextGadget(#lblResponse, x, y+4, lblw, lblh, "Click Response")
x + lblw
StringGadget(#txtResponse, x, y, txtw, txth, "")
; List gadget
y + txth+ gap*2: x = gap
ListViewGadget(#lstHello, x, y, lstw, lsth)
; Buttons
y + lsth+gap*2: x = gap
ButtonGadget(#butNonthread, x, y, butw, buth, "Run Nonthreaded")
x = (winw-butw)/2
ButtonGadget(#butThreaded, x, y, butw, buth, "Run Multithreaded")
y + buth+gap: x = butw/2+gap*3/2
ButtonGadget(#butTest, x, y, butw, buth, "Test Responsiveness")
x = winw-butw-gap
ButtonGadget(#butExit, x, y, butw, buth, "E x i t")
; Event loop
Define done=#False
Repeat
Select WaitWindowEvent()
Case #PB_Event_Gadget, #PB_Event_Menu
Select EventGadget() ; Or EventMenu()
Case #butNonthread
; Respond to the nonThreadedButton click event
FillList(1)
Case #butThreaded
; Respond to the multiThreadedButton click event
; Launch a thread to do the update
CreateThread(@FillList(), 2)
Case #butTest
; Respond to the testResponseButton click event
SetGadgetText(#txtResponse, Str(Val(GetGadgetText(#txtResponse))+1))
Case #butExit ; Exit button
done=#True
EndSelect
Case #PB_Event_CloseWindow
done=#True
EndSelect
Until done
End
Procedure FillList(*value)
Protected i
; Populate list box
Debug "Value = "+Str(*value)
ClearGadgetItems(#lstHello)
SetGadgetText(#txtResponse, "0")
ToggleButtons(#False)
For i = 0 To 9
AddGadgetItem(#lstHello, -1, "Hello World " + Str(i))
Delay(1000)
Next i
ToggleButtons(#True)
EndProcedure
Procedure ToggleButtons(toggle)
; Toggle the form buttons on or off accordingly.
DisableGadget(#butNonthread, #True-toggle)
DisableGadget(#butThreaded, #True-toggle)
EndProcedure
Re: Multithreading Demo
Posted: Sat Feb 19, 2011 12:59 pm
by ABBKlaus
You should not exit the main loop while the thread is running !
BR Klaus
Re: Multithreading Demo
Posted: Sat Feb 19, 2011 4:25 pm
by idle
you should be checking for the threads existence and waiting for it to terminate.
Code: Select all
; Multithreaded Response
; www.codeguru.com/columns/dotnet/article.php/c4593
; When either the "Run Nonthreaded" or "Run Multithreaded" buttons are pressed
; they will be temporarily disabled and a list box will be filled with
; "Hello World" messages with a delay between each.
; The window also contains a button to test the interface responsiveness while
; the list box is being populated.
; The click events of the "Run Nonthreaded" and "Run Multithreaded" buttons
; result in the same procedure being executed. The only difference is that when
; the "Run Multithreaded" button is pressed the method is executed in a new thread.
#Program$ = "Multithreaded Response"
#Version$ = "1.0"
EnableExplicit
; Constants
Enumeration
#winMain
#lblResponse
#txtResponse
#lstHello
#butNonthread
#butThreaded
#butTest
#butExit
EndEnumeration
Declare FillList(*value)
Declare ToggleButtons(toggle)
; GUI Metrics
Define gap, lstw, lsth, butw, buth, winw, winh
Define lblw, lblh, txtw, txth,thread
gap = 20
lblw = 100: lblh = 24
txtw = 100: txth = lblh
lstw = 500: lsth = 200
butw = 150: buth = 30
winw = lstw+gap*2: winh = txth+lsth+buth*2+gap*7
; Create GUI
Define title$, flags, x, y
title$ = " "+#Program$+" "+#Version$
flags = #PB_Window_SystemMenu|#PB_Window_MinimizeGadget|#PB_Window_TitleBar|#PB_Window_ScreenCentered
OpenWindow(#winMain, 0, 0, winw, winh, title$, flags)
; Labelled text box
y = gap: x = gap
TextGadget(#lblResponse, x, y+4, lblw, lblh, "Click Response")
x + lblw
StringGadget(#txtResponse, x, y, txtw, txth, "")
; List gadget
y + txth+ gap*2: x = gap
ListViewGadget(#lstHello, x, y, lstw, lsth)
; Buttons
y + lsth+gap*2: x = gap
ButtonGadget(#butNonthread, x, y, butw, buth, "Run Nonthreaded")
x = (winw-butw)/2
ButtonGadget(#butThreaded, x, y, butw, buth, "Run Multithreaded")
y + buth+gap: x = butw/2+gap*3/2
ButtonGadget(#butTest, x, y, butw, buth, "Test Responsiveness")
x = winw-butw-gap
ButtonGadget(#butExit, x, y, butw, buth, "E x i t")
; Event loop
Define done=#False
Repeat
Select WaitWindowEvent()
Case #PB_Event_Gadget, #PB_Event_Menu
Select EventGadget() ; Or EventMenu()
Case #butNonthread
; Respond to the nonThreadedButton click event
FillList(1)
Case #butThreaded
; Respond to the multiThreadedButton click event
; Launch a thread to do the update
If Not IsThread(thread)
thread = CreateThread(@FillList(), 2)
EndIf
Case #butTest
; Respond to the testResponseButton click event
SetGadgetText(#txtResponse, Str(Val(GetGadgetText(#txtResponse))+1))
Case #butExit ; Exit button
done=#True
EndSelect
Case #PB_Event_CloseWindow
done=#True
EndSelect
Until done
If IsThread(thread)
WaitThread(thread)
EndIf
End
Procedure FillList(*value)
Protected i
; Populate list box
Debug "Value = "+Str(*value)
ClearGadgetItems(#lstHello)
SetGadgetText(#txtResponse, "0")
ToggleButtons(#False)
For i = 0 To 9
AddGadgetItem(#lstHello, -1, "Hello World " + Str(i))
Delay(1000)
Next i
ToggleButtons(#True)
EndProcedure
Procedure ToggleButtons(toggle)
; Toggle the form buttons on or off accordingly.
DisableGadget(#butNonthread, #True-toggle)
DisableGadget(#butThreaded, #True-toggle)
EndProcedure
Re: Multithreading Demo
Posted: Sat Feb 19, 2011 5:39 pm
by ABBKlaus
idle, basically you are doing the same thing as akj.
Both of you are leaving the main loop without waiting for the thread has ended.
You will see if you add a closewindow() after the Repeat/Until loop.
A crash like this will occur : The specified #Gadget is not initialized.
This code should demonstrate what i mean
Code: Select all
; Multithreaded Response
; www.codeguru.com/columns/dotnet/article.php/c4593
; When either the "Run Nonthreaded" or "Run Multithreaded" buttons are pressed
; they will be temporarily disabled and a list box will be filled with
; "Hello World" messages with a delay between each.
; The window also contains a button to test the interface responsiveness while
; the list box is being populated.
; The click events of the "Run Nonthreaded" and "Run Multithreaded" buttons
; result in the same procedure being executed. The only difference is that when
; the "Run Multithreaded" button is pressed the method is executed in a new thread.
#Program$ = "Multithreaded Response"
#Version$ = "1.0"
EnableExplicit
; Constants
Enumeration
#winMain
#lblResponse
#txtResponse
#lstHello
#butNonthread
#butThreaded
#butTest
#butExit
EndEnumeration
Structure STR_Thread
ThreadID.i
ThreadData.i
ThreadQuit.i
ThreadEnd.i
EndStructure
Declare FillList(*Thread.STR_Thread)
Declare ToggleButtons(toggle)
Procedure FillList(*Thread.STR_Thread)
Protected i
; Populate list box
Debug "FillList()"
Debug "ThreadData = "+Str(*Thread\ThreadData)
ClearGadgetItems(#lstHello)
SetGadgetText(#txtResponse, "0")
ToggleButtons(#False)
For i = 0 To 9
AddGadgetItem(#lstHello, -1, "Hello World " + Str(i))
; check if thread should exit
If *Thread\ThreadQuit
Break
EndIf
Delay(1000)
Next i
ToggleButtons(#True)
Debug "FillList() end"
EndProcedure
Procedure ToggleButtons(toggle)
; Toggle the form buttons on or off accordingly.
DisableGadget(#butNonthread, #True-toggle)
DisableGadget(#butThreaded, #True-toggle)
EndProcedure
; GUI Metrics
Define gap, lstw, lsth, butw, buth, winw, winh
Define lblw, lblh, txtw, txth
Define thread.STR_Thread
gap = 20
lblw = 100: lblh = 24
txtw = 100: txth = lblh
lstw = 500: lsth = 200
butw = 150: buth = 30
winw = lstw+gap*2: winh = txth+lsth+buth*2+gap*7
; Create GUI
Define title$, flags, x, y
title$ = " "+#Program$+" "+#Version$
flags = #PB_Window_SystemMenu|#PB_Window_MinimizeGadget|#PB_Window_TitleBar|#PB_Window_ScreenCentered
OpenWindow(#winMain, 0, 0, winw, winh, title$, flags)
; Labelled text box
y = gap: x = gap
TextGadget(#lblResponse, x, y+4, lblw, lblh, "Click Response")
x + lblw
StringGadget(#txtResponse, x, y, txtw, txth, "")
; List gadget
y + txth+ gap*2: x = gap
ListViewGadget(#lstHello, x, y, lstw, lsth)
; Buttons
y + lsth+gap*2: x = gap
ButtonGadget(#butNonthread, x, y, butw, buth, "Run Nonthreaded")
x = (winw-butw)/2
ButtonGadget(#butThreaded, x, y, butw, buth, "Run Multithreaded")
y + buth+gap: x = butw/2+gap*3/2
ButtonGadget(#butTest, x, y, butw, buth, "Test Responsiveness")
x = winw-butw-gap
ButtonGadget(#butExit, x, y, butw, buth, "E x i t")
AddWindowTimer(#winMain,1,100)
; Event loop
Define done=#False
Repeat
Select WaitWindowEvent()
Case #PB_Event_Timer
If thread\ThreadID
If IsThread(thread\ThreadID)
Debug "Thread is running"
Else
Debug "Thread has ended"
thread\ThreadID=0
EndIf
EndIf
Case #PB_Event_Gadget, #PB_Event_Menu
Select EventGadget() ; Or EventMenu()
Case #butNonthread
; Respond to the nonThreadedButton click event
FillList(thread)
Case #butThreaded
; Respond to the multiThreadedButton click event
; Launch a thread to do the update
If Not IsThread(thread\ThreadID)
thread\ThreadID = CreateThread(@FillList(), thread)
EndIf
Case #butTest
; Respond to the testResponseButton click event
SetGadgetText(#txtResponse, Str(Val(GetGadgetText(#txtResponse))+1))
Case #butExit ; Exit button
done=#True
thread\ThreadQuit=1
EndSelect
Case #PB_Event_CloseWindow
done=#True
thread\ThreadQuit=1
EndSelect
Until done And thread\ThreadID=0
CloseWindow(#winMain) ; see if we have coded threading the correct way
BR Klaus
Re: Multithreading Demo
Posted: Sat Feb 19, 2011 6:33 pm
by idle
Thats better thanks, it was what I meant to do but I shouldn't be posting code at 4:20 am in the morning!

Re: Multithreading Demo
Posted: Sat Feb 19, 2011 10:28 pm
by ts-soft
For me, this isn't multithreaded, is only threaded.
In multithreaded, i can start more as one of the same thread at the same time.
Re: Multithreading Demo
Posted: Sun Feb 20, 2011 2:32 am
by ABBKlaus
I completely disagree with your point thomas, the above example is mulithreaded just like the wikipedia descibes it.
BR Klaus
Re: Multithreading Demo
Posted: Sun Feb 20, 2011 3:14 am
by ts-soft
In every time there run only one thread, never more. This is single threaded for me. I can't read the english wiki.
In multithreaded application you can run more as one thread at the same time and you require mutex or semaphore to control the access of linklist.
Re: Multithreading Demo
Posted: Sun Feb 20, 2011 9:00 am
by cas
Main loop is one thread, with CreateThread() you open another thread. So in this example, when you press "Run Multithreaded" button, you have two threads running at same time --> multithreaded.
But this example can be done in single thread mode (with responsive gui) with help of AddWindowTimer().
Re: Multithreading Demo
Posted: Sun Feb 20, 2011 2:51 pm
by Thorium
Multithreading gets realy interessting with a job queue so it takes advantage of multiple cores.
Dont need a demo to start 2 threads. ^^
Re: Multithreading Demo
Posted: Sun Feb 20, 2011 6:06 pm
by skywalk
cas wrote:But this example can be done in single thread mode (with responsive gui) with help of AddWindowTimer().

I thought an AddWindowTimer() would be tied to the main thread?
So if a non-responsive Procedure() had control, are you saying a window timer would still fire an event in the main event loop?
I was going to ask Trond the same question in his post...
http://www.purebasic.fr/english/viewtop ... 13#p347013
He lists several options to address long procedure calls hanging the GUI, but no AddWindowTimer()?
Re: Multithreading Demo
Posted: Sun Feb 20, 2011 10:03 pm
by cas
I said that you can use AddWindowTimer() for this example because there is no additional processing:
Code: Select all
#Program$ = "Not Multithreaded Response"
#Version$ = "1.0"
EnableExplicit
; Constants
Enumeration
#winMain
#lblResponse
#txtResponse
#lstHello
#butNonthread
#butThreaded
#butTest
#butExit
EndEnumeration
Declare ToggleButtons(toggle)
; GUI Metrics
Define gap, lstw, lsth, butw, buth, winw, winh
Define lblw, lblh, txtw, txth
gap = 20
lblw = 100: lblh = 24
txtw = 100: txth = lblh
lstw = 500: lsth = 200
butw = 150: buth = 30
winw = lstw+gap*2: winh = txth+lsth+buth*2+gap*7
; Create GUI
Define title$, flags, x, y
title$ = " "+#Program$+" "+#Version$
flags = #PB_Window_SystemMenu|#PB_Window_MinimizeGadget|#PB_Window_TitleBar|#PB_Window_ScreenCentered
OpenWindow(#winMain, 0, 0, winw, winh, title$, flags)
; Labelled text box
y = gap: x = gap
TextGadget(#lblResponse, x, y+4, lblw, lblh, "Click Response")
x + lblw
StringGadget(#txtResponse, x, y, txtw, txth, "")
; List gadget
y + txth+ gap*2: x = gap
ListViewGadget(#lstHello, x, y, lstw, lsth)
; Buttons
y + lsth+gap*2: x = gap
ButtonGadget(#butNonthread, x, y, butw, buth, "Run Nonthreaded")
x = (winw-butw)/2
;ButtonGadget(#butThreaded, x, y, butw, buth, "Run Multithreaded")
y + buth+gap: x = butw/2+gap*3/2
ButtonGadget(#butTest, x, y, butw, buth, "Test Responsiveness")
x = winw-butw-gap
ButtonGadget(#butExit, x, y, butw, buth, "E x i t")
; Event loop
Define done=#False
Global add_counter
Repeat
Select WaitWindowEvent()
Case #PB_Event_Gadget, #PB_Event_Menu
Select EventGadget() ; Or EventMenu()
Case #butNonthread
; Respond to the nonThreadedButton click event
;FillList(1)
ClearGadgetItems(#lstHello)
SetGadgetText(#txtResponse, "0")
ToggleButtons(#False)
Case #butTest
; Respond to the testResponseButton click event
SetGadgetText(#txtResponse, Str(Val(GetGadgetText(#txtResponse))+1))
Case #butExit ; Exit button
done=#True
EndSelect
Case #PB_Event_Timer
If EventTimer()=0
AddGadgetItem(#lstHello, -1, "Hello World " + Str(add_counter))
add_counter+1
If add_counter=10
ToggleButtons(#True)
EndIf
EndIf
Case #PB_Event_CloseWindow
done=#True
EndSelect
Until done
End
Procedure ToggleButtons(toggle)
DisableGadget(#butNonthread, #True-toggle)
If toggle=0
AddWindowTimer(#winMain,0,1000)
Else
add_counter=0
RemoveWindowTimer(#winMain,0)
EndIf
EndProcedure
And it is not recommended to modify gadgets from secondary thread. Gadgets should be updated only from main thread. So if you have background procedure processing something then use SendMessage_() and update gadgets in window callback.
Re: Multithreading Demo
Posted: Mon Feb 21, 2011 5:49 pm
by ABBKlaus
cas wrote:And it is not recommended to modify gadgets from secondary thread. Gadgets should be updated only from main thread. So if you have background procedure processing something then use SendMessage_() and update gadgets in window callback.
where do you read that, please explain (i already searched the whole forum) ?
The only thing that i know of is this :
http://www.purebasic.com/documentation/ ... indow.html
Note: When opening a Window from a thread, the thread must also call WindowEvent() or WaitWindowEvent() in a loop to process events for this window, as window events are not sent between different threads.
BR Klaus
Re: Multithreading Demo
Posted: Mon Feb 21, 2011 6:36 pm
by cas
Re: Multithreading Demo
Posted: Mon Feb 21, 2011 10:15 pm
by ABBKlaus
Yes i know that thread, f34k is explaing the wrong use of LockMutex().
I do not read somewhere that you should not use threads to modify gadgets !
Lets get the eventqeue running
