ChristianH hat geschrieben:
Hallo Michael,
danke für den Hinweis. Wie Du sicher unschwer an meinem Code erkennen kannst wird das etwas dauern bis ich Dein Beispiel verstehen/begreifen kann.
Zitat:
"Bei macOS und Linux darf man aus Thread nicht die Gadgets aktualisieren."
Warum darf man das nicht?
Gruß Christian
Führt zum Crash da diese vom der OS nicht Threadsafe sind.Sorry, hatte etwas zu viel Langeweile
Noch nicht getestet
Code:
;-TOP
;- Begin Mini Thread Control
; by mk-soft, Version 1.08, 20.10.2019
CompilerIf Not #PB_Compiler_Thread
CompilerError "Use Compiler-Option ThreadSafe!"
CompilerEndIf
Structure udtThreadControl
ThreadID.i
UserID.i
Signal.i
Pause.i
Exit.i
EndStructure
Procedure StartThread(*Data.udtThreadControl, *Procedure) ; ThreadID
If Not IsThread(*Data\ThreadID)
*Data\Exit = #False
*Data\Pause = #False
*Data\ThreadID = CreateThread(*Procedure, *Data)
EndIf
ProcedureReturn *Data\ThreadID
EndProcedure
Procedure StopThread(*Data.udtThreadControl, Wait = 1000) ; Void
If IsThread(*Data\ThreadID)
*Data\Exit = #True
If *Data\Pause
*Data\Pause = #False
SignalSemaphore(*Data\Signal)
EndIf
If Wait
If WaitThread(*Data\ThreadID, Wait) = 0
KillThread(*Data\ThreadID)
EndIf
*Data\ThreadID = 0
*Data\Pause = #False
*Data\Exit = #False
If *Data\Signal
FreeSemaphore(*Data\Signal)
*Data\Signal = 0
EndIf
EndIf
EndIf
EndProcedure
Procedure FreeThread(*Data.udtThreadControl, Stop = #True, Wait = 1000) ; True or False
If IsThread(*Data\ThreadID)
If Stop
StopThread(*Data, Wait)
FreeStructure(*Data)
ProcedureReturn #True
Else
ProcedureReturn #False
EndIf
Else
If *Data\Signal
FreeSemaphore(*Data\Signal)
EndIf
FreeStructure(*Data)
ProcedureReturn #True
EndIf
EndProcedure
Procedure ThreadPause(*Data.udtThreadControl) ; Void
If IsThread(*Data\ThreadID)
If Not *Data\Signal
*Data\Signal = CreateSemaphore()
EndIf
If Not *Data\Pause
*Data\Pause = #True
EndIf
EndIf
EndProcedure
Procedure ThreadResume(*Data.udtThreadControl) ; Void
If IsThread(*Data\ThreadID)
If *Data\Pause
*Data\Pause = #False
SignalSemaphore(*Data\Signal)
EndIf
EndIf
EndProcedure
;- End Mini Thread Control
; ****
;- UDP Server
InitNetwork()
; ---- String Helper ----
Procedure AllocateString(String.s) ; Result = Pointer
Protected *mem.string = AllocateStructure(String)
If *mem
*mem\s = String
EndIf
ProcedureReturn *mem
EndProcedure
Procedure.s FreeString(*mem.string) ; Result String
Protected r1.s
If *mem
r1 = *mem\s
FreeStructure(*mem)
EndIf
ProcedureReturn r1
EndProcedure
; ----
Enumeration #PB_Event_FirstCustomValue
#MyEvent_ThreadSendString
#MyEvent_ThreadFinished
EndEnumeration
Enumeration Window
#Main
EndEnumeration
Enumeration Gadget
#ListLogging
#StringData1
#StringData2
#ButtonStart
#ButtonPauseResume
#ButtonStop
EndEnumeration
; Extends always own data structure with structure from thread control
Structure udtThreadData Extends udtThreadControl
; GUI Data
Window.i
Logging.i
Data1.i
Data2.i
; Server
Server.i
Port.i
EndStructure
Declare MyThread(*Data.udtThreadData)
; Create Data always with AllocateStructure
Global *th1.udtThreadData = AllocateStructure(udtThreadData)
*th1\UserID = 1
*th1\Window = #Main
*th1\Logging = #ListLogging
*th1\Data1 = #StringData1
*th1\Data2 = #StringData2
*th1\Port = 6600
Procedure Main()
Protected info.s
If OpenWindow(#Main, 50, 50, 600, 400, "Mini Thread Control", #PB_Window_SystemMenu)
ListViewGadget(#ListLogging, 5, 5, 590, 320)
StringGadget(#StringData1, 5, 330, 290, 25, "", #PB_String_ReadOnly)
StringGadget(#StringData2, 305, 330, 290, 25, "", #PB_String_ReadOnly)
ButtonGadget(#ButtonStart, 5, 365, 120, 30, "Start")
ButtonGadget(#ButtonPauseResume, 130, 365, 120, 30, "Pause")
ButtonGadget(#ButtonStop, 255, 365, 120, 30, "Stop")
DisableGadget(#ButtonPauseResume, #True)
Repeat
Select WaitWindowEvent()
Case #PB_Event_CloseWindow
FreeThread(*th1)
Break
Case #PB_Event_Gadget
Select EventGadget()
Case #ButtonStart
StartThread(*th1, @MyThread())
Case #ButtonPauseResume
If IsThread(*th1\ThreadID)
If Not *th1\Pause
ThreadPause(*th1)
SetGadgetText(#ButtonPauseResume, "Resume")
Else
ThreadResume(*th1)
SetGadgetText(#ButtonPauseResume, "Pause")
EndIf
EndIf
Case #ButtonStop
StopThread(*th1)
SetGadgetText(#ButtonPauseResume, "Pause")
EndSelect
Case #MyEvent_ThreadSendString
Select EventGadget()
Case #ListLogging
; Receive string over event data
AddGadgetItem(#ListLogging, -1, FreeString(EventData()))
; Small trick to move last item
SetGadgetState(#ListLogging, CountGadgetItems(#ListLogging) - 1)
SetGadgetState(#ListLogging, -1)
Case #StringData1
info = FreeString(EventData())
SetGadgetText(#StringData1, info + " °C")
Case #StringData2
info = FreeString(EventData())
SetGadgetText(#StringData2, info + " ??")
EndSelect
EndSelect
ForEver
EndIf
EndProcedure : Main()
End
Procedure MyThread(*Data.udtThreadData)
Protected stringdata.s, cnt
Protected *buffer, size
Protected info.s
; Send string over PostEvent and parameter EventData
With *Data
stringdata = "Init Server " + \UserID
PostEvent(#MyEvent_ThreadSendString, \Window, \Logging, 0, AllocateString(stringdata))
;TODO
\Server = CreateNetworkServer(#PB_Any, \Port, #PB_Network_UDP)
If \Server = 0
stringdata = "Error: Create Server on Port " + \Port
PostEvent(#MyEvent_ThreadSendString, \Window, \Logging, 0, AllocateString(stringdata))
stringdata = "Finished Thread " + \UserID
PostEvent(#MyEvent_ThreadSendString, \Window, \Logging, 0, AllocateString(stringdata))
\ThreadID = 0
ProcedureReturn 0
EndIf
*buffer = AllocateMemory(2048)
stringdata = "Start Server " + \UserID
PostEvent(#MyEvent_ThreadSendString, \Window, \Logging, 0, AllocateString(stringdata))
;TODO
Repeat
; 1. Query on thread pause
If \Pause
stringdata = "Pause Thread " + \UserID
PostEvent(#MyEvent_ThreadSendString, \Window, \Logging, 0, AllocateString(stringdata))
WaitSemaphore(\Signal)
stringdata = "Resume Thread " + \UserID
PostEvent(#MyEvent_ThreadSendString, \Window, \Logging, 0, AllocateString(stringdata))
EndIf
; 2. Query on thread cancel
If \Exit
Break
EndIf
;TODO Cyle Process
Select NetworkServerEvent()
Case #PB_NetworkEvent_None
Delay(100)
Case #PB_NetworkEvent_Connect ; Only TCP/IP
;
Case #PB_NetworkEvent_Disconnect ; Only TCP/IP
; ;
Case #PB_NetworkEvent_Data
size = ReceiveNetworkData(EventClient(), *buffer, 2048)
info = (PeekS(*buffer, size, #PB_Ascii))
If Left(info, 11) = "Temperature"
stringdata = Right(info, 4)
PostEvent(#MyEvent_ThreadSendString, \Window, \Data1, 0, AllocateString(stringdata))
EndIf
If Left(info, 8) = "Humidity"
stringdata = Right(info, 4)
PostEvent(#MyEvent_ThreadSendString, \Window, \Data2, 0, AllocateString(stringdata))
EndIf
EndSelect
ForEver
If \Exit
;TODO Thread Cancel
stringdata = "Cancel Thread " + \UserID
PostEvent(#MyEvent_ThreadSendString, \Window, \Logging, 0, AllocateString(stringdata))
Else
;TODO Thread Finished
stringdata = "Finished Thread " + \UserID
PostEvent(#MyEvent_ThreadSendString, \Window, \Logging, 0, AllocateString(stringdata))
EndIf
stringdata = "Close Server " + \UserID
PostEvent(#MyEvent_ThreadSendString, \Window, \Logging, 0, AllocateString(stringdata))
CloseNetworkServer(\Server)
FreeMemory(*buffer)
; 3. Clear ThreadID
\ThreadID = 0
EndWith
EndProcedure