[Linux] Thread-Interface mit join

Hier könnt Ihr gute, von Euch geschriebene Codes posten. Sie müssen auf jeden Fall funktionieren und sollten möglichst effizient, elegant und beispielhaft oder einfach nur cool sein.
Benutzeravatar
NicTheQuick
Ein Admin
Beiträge: 8812
Registriert: 29.08.2004 20:20
Computerausstattung: Ryzen 7 5800X, 64 GB DDR4-3200
Ubuntu 24.04.2 LTS
GeForce RTX 3080 Ti
Wohnort: Saarbrücken

[Linux] Thread-Interface mit join

Beitrag von NicTheQuick »

Hallo,

da ich gerade noch nicht schlafen konnte, habe ich noch Threads als Interface implementiert. Interessant ist daran zum Beispiel, dass es die hübsche Methode join() gibt, die ähnlich WaitThread() ist, aber den Rückgabewert eines Threads liefert. Leider funktioniert dafür aber das optionale Timeout, was es ja bei WaitThread() gibt, (noch) nicht.

Bisher konnte ich das ganze nur unter Ubuntu 10.04 x64 testen. Ich würde mich aber freuen, wenn jemand das ganze mal noch auf einem anderen Linux testet. Es könnte passieren, dass jemand die Library '/usr/lib/libglibmm-2.4.so' nicht installiert hat, aber bei mir ging es nur mit dieser einen. Leider sind auch dort nicht alle Befehle integriert, die ich gerne getestet hätte, wie z.B. g_thread_yield(). Vielleicht weiß da jemand genaueres oder kann mir sagen, welche Library ich bei 'ImportC' in Zeile 42 des Includes wählen muss, damit es auch wirklich bei jedem out-of-the-box funktioniert.

Bei Gelegenheit oder durch Mithilfe anderer programmierfreudiger Boardnutzer wäre als nächstes angedacht das Interface auch für Windows anzupassen, damit man am Ende eine Include hat, die alles kann. Als nächstes würden dann Mutexe, Conditions und evtl. das in glib integrierte Reader-Writer-Lock als weitere Includes anstehen.

Weitere Informationen zur 'glib' finden sich hier: glib-Threads

Es folgen nun wie immer zuerst die Include und dann ein Beispiel.

Thread.pbi

Code: Alles auswählen

EnableExplicit

Interface Thread
	free.i()
	currentThread.i()
	getName.s()
	getPriority.i()
	isAlive.i()
	join.i()
	run.i()
	setName.i(name.s)
	setPriority.i(newPriority.i)
	getErrorMessage.s()
	; It is Not guaranteed that threads With different priorities really behave
	; accordingly. On some systems (e.g. Linux) there are no thread priorities.
	; On other systems (e.g. Solaris) there doesn't seem to be different
	; scheduling For different priorities. All in all try To avoid being
	; dependent on priorities.
	start.i()
EndInterface

Prototype.i Thread_Prototype(*threadData)

Structure Thread_S
	*vTable
	*hThread.GThread
	*function.Thread_Prototype
	*pError.Integer
	name.s
	priority.i
	interrupted.i
	*threadData
EndStructure

Enumeration
	#G_THREAD_PRIORITY_LOW
	#G_THREAD_PRIORITY_NORMAL
	#G_THREAD_PRIORITY_HIGH
	#G_THREAD_PRIORITY_URGENT
EndEnumeration

ImportC "/usr/lib/libglibmm-2.4.so"
	g_thread_init(*vTable)
	g_thread_get_initialized.i()
	g_thread_create_full.i(*func, *threadData, stack_size.i, joinable.i, bound.i, priority.i, *pError)
	g_thread_self.i()
	g_thread_join.i(*thread.GTHREAD)
	;g_thread_yield()
	g_thread_set_priority(*thread.GTHREAD, priority.i)
	g_thread_exit(*returnValue)
EndImport

Procedure.s Thread_getErrorMessage(*this.Thread_S)
	Protected *error.GERROR, msg.s
	
	*error = *this\pError\i
	msg = "domain: " + Str(*error\domain) + #LF$
	msg + "code: " + Str(*Error\code) + #LF$
	msg + "message: " + PeekS(*error, #PB_Ascii)
	
	ProcedureReturn msg
EndProcedure

Procedure.i newThread(*function.Thread_Prototype, *threadData = #Null, name.s = "")
	Protected *this.Thread_S
	
	*this = AllocateMemory(SizeOf(Thread_S))
	If (Not *this)
		ProcedureReturn
	EndIf
	
	InitializeStructure(*this, Thread_S)
	
	If (Not g_thread_get_initialized())
		g_thread_init(#Null)
	EndIf
	
	With *this
		\vTable = ?Thread_vTable
		\threadData = *threadData
		\function = *function
		\pError = AllocateMemory(SizeOf(Integer))
		\hThread = 0;
		\name = name
		\priority = #G_THREAD_PRIORITY_NORMAL
		\interrupted = #False
	EndWith
	
	ProcedureReturn *this
EndProcedure

Procedure.i Thread_currentThread(*this.Thread_S = 0)
	Protected *gThread.GTHREAD
	If (Not *this)
		*gThread = g_thread_self()
		*this = *gThread\data
	EndIf
	
	ProcedureReturn *this
EndProcedure

Procedure.i Thread_join(*this.Thread_S)
	Protected *returnValue
	
	With *this
		If (Not \hThread)
			ProcedureReturn #False
		EndIf
	
		*returnValue = g_thread_join(\hThread)
	EndWith
	
	ProcedureReturn *returnValue
EndProcedure

Procedure.i Thread_setPriority(*this.Thread_S, priority.i)
	With *this
		If (Not \hThread)
			ProcedureReturn #False
		EndIf
		\priority = priority
		g_thread_set_priority(\hThread, \priority)
	
	EndWith
	
	ProcedureReturn #True
EndProcedure

Procedure.s Thread_getName(*this.Thread_S = 0)
	If (Not *this)
		*this = Thread_currentThread()
	EndIf
	
	If (*this)
		ProcedureReturn *this\name
	EndIf
	
	ProcedureReturn "[NO THREAD]"
EndProcedure

Procedure.i Thread_setName(*this.Thread_S, name.s)
	*this\name = name
	
	ProcedureReturn #True
EndProcedure

Procedure.i Thread_run(*this.Thread_S)
	Protected *returnValue
	With *this
		*returnValue = \function(\threadData)
		\hThread = 0
	EndWith
	
	ProcedureReturn *returnValue
EndProcedure

Procedure.i Thread_start(*this.Thread_S)
	With *this
		\hThread = g_thread_create_full(@Thread_run(), *this, SizeOf(Integer), #True, #False, \priority, \pError)
		If (\hThread = #Null)
			ProcedureReturn #False
		EndIf
	EndWith
EndProcedure

Procedure.i Thread_isAlive(*this.Thread_S)
	If (*this\hThread)
		ProcedureReturn #True
	EndIf
	
	ProcedureReturn #False
EndProcedure

Procedure.i Thread_getPriority(*this.Thread_S = 0)
	If (Not *this)
		*this = Thread_currentThread()
	EndIf
	
	If (*this)
		ProcedureReturn *this\priority
	EndIf
EndProcedure

Procedure.i Thread_free(*this.Thread_S)
	Protected *returnValue
	
	*returnValue = Thread_join(*this)
	
	FreeMemory(*this\pError)
	
	ClearStructure(*this, Thread_S)
	
	ProcedureReturn *returnValue
EndProcedure

DataSection
	Thread_vTable:
		Data.i @Thread_free()			;done
		Data.i @Thread_currentThread()	;done
		Data.i @Thread_getName()		;done
		Data.i @Thread_getPriority()	;done
		Data.i @Thread_isAlive()		;done
		Data.i @Thread_join()			;done
		Data.i @Thread_run()			;done
		Data.i @Thread_setName()		;done
		Data.i @Thread_setPriority()	;done
		Data.i @Thread_getErrorMessage();done
		Data.i @Thread_start()			;done
EndDataSection
Thread_Example.pbi

Code: Alles auswählen

XIncludeFile "Thread.pbi"

Procedure.i testThread(*threadData)
	Delay(200)
	
	Debug "*threadData = " + Str(*threadData)
	Debug "My name is '" + Thread_getName() + "' and my priority is '" + Str(Thread_getPriority()) + "'"
	
	Delay(1000)
	
	Debug "Thread ends"	
	ProcedureReturn 456
EndProcedure

Define thread.Thread = newThread(@testThread(), 123, "Thread 1")

Debug "Start thread '" + thread\getName() + "'"
thread\start()

Debug "isAlive() = " + Str(thread\isAlive())

Delay(100)

Debug "Thread should still run"
Debug "isAlive() = " + Str(thread\isAlive())

Debug "Return value from thread: " + Str(thread\join())
Debug "isAlive() = " + Str(thread\isAlive())

Debug "End of the program"
Debug "Start thread again (no free())"
thread\start()

Debug "Return value from free(): " + Str(thread\free())