Multi Threads

Anfängerfragen zum Programmieren mit PureBasic.
Benutzeravatar
_JON_
Beiträge: 389
Registriert: 30.03.2010 15:24

Re: Multi Threads

Beitrag von _JON_ »

Wow, danke Nic, läuft genauso wie es soll. :D

EDIT: Ab und zu bekomme ich einen IMA bei "WaitSemaphore(\doneSema)"
PureBasic 5.46 LTS (Windows x86/x64) | windows 10 x64 Oktober failure
Benutzeravatar
NicTheQuick
Ein Admin
Beiträge: 8809
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

Re: Multi Threads

Beitrag von NicTheQuick »

Hast du Thread-Unterstützung aktiviert?
Benutzeravatar
_JON_
Beiträge: 389
Registriert: 30.03.2010 15:24

Re: Multi Threads

Beitrag von _JON_ »

Oh, das wars, mit Thread safe läuft es perfekt :allright:
PureBasic 5.46 LTS (Windows x86/x64) | windows 10 x64 Oktober failure
Benutzeravatar
NicTheQuick
Ein Admin
Beiträge: 8809
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

Re: Multi Threads

Beitrag von NicTheQuick »

Das freut mich. Übrigens kannst du auch zwischendrin neue Aufgaben mit addThread(*block, @proc()) hinzufügen. Beim nächsten Aufruf von processBlock() wird dann auch diese Aufgabe ausgeführt.
Und vergiss am Ende das destroyBlock(*block) nicht. :allright:
Benutzeravatar
ts-soft
Beiträge: 22292
Registriert: 08.09.2004 00:57
Computerausstattung: Mainboard: MSI 970A-G43
CPU: AMD FX-6300 Six-Core Processor
GraKa: GeForce GTX 750 Ti, 2 GB
Memory: 16 GB DDR3-1600 - Dual Channel
Wohnort: Berlin

Re: Multi Threads

Beitrag von ts-soft »

Dann steht ja dem nächstem Kartoffelgratin nichts mehr im Wege :allright:
PureBasic 5.73 LTS | SpiderBasic 2.30 | Windows 10 Pro (x64) | Linux Mint 20.1 (x64)
Nutella hat nur sehr wenig Vitamine. Deswegen muss man davon relativ viel essen.
Bild
Benutzeravatar
_JON_
Beiträge: 389
Registriert: 30.03.2010 15:24

Re: Multi Threads

Beitrag von _JON_ »

Dein Code is echt genial Nic, nochmals Danke.

Ich habe mal ein praktisches Beispiel mit PB's Fingerprint Funktion erstellt.
Mit den Threads läuft es hier ungefähr doppelt so schnell als ohne. :D

Code: Alles auswählen

EnableExplicit

Define *Buffer, iRead, x, sfile.s, CRC$, MD5$, SHA1$ 
#Buffersize = 8192

Prototype Aufgabe(*buffer, bufferSize.i, iBlock.i)

UseCRC32Fingerprint()
UseMD5Fingerprint()
UseSHA1Fingerprint()


Structure Block
   *buffer       ; Pointer zum Puffer selbst
   size.i        ; Größe des Puffers
   iBlock.i      ; Nummer des Blocks
   waitSema.i    ; Semaphore um auf Befüllen den Blockes zu warten
   doneSema.i    ; Semaphore um auf Beendigung der Verarbeitung zu warten
   allDoneSema.i ; 
   stop.i        ; #True, wenn der Thread sich beenden soll
   List threads.i()
   cThreads.i    ; Anzahl der Threads
EndStructure

Procedure.i newBlock()
   Protected *block.Block = AllocateStructure(Block)
   
   With *block
      \waitSema = CreateSemaphore()
      \doneSema = CreateSemaphore()
      \allDoneSema = CreateSemaphore()
   EndWith
   
   ProcedureReturn *block
EndProcedure

Structure ThreadData
   *block.Block
   aufgabe.Aufgabe
EndStructure

Procedure AufgabeThread(*data.ThreadData)
   With *data\block
      Repeat
         ; Warte bis ein neuer Block da ist
         WaitSemaphore(\waitSema)
         
         If \stop
            Break
         EndIf
         
         ; Führe die Aufgabe auf dem Block aus
         *data\aufgabe(\buffer, \size, \iBlock)
         
         ; Melde dem Hauptthread, dass du fertig bist
         SignalSemaphore(\doneSema)
         
         ; Warte auf den Hauptthread, der dir mitteilt, das alle anderen Threads auch fertig sind
         WaitSemaphore(\allDoneSema)
      ForEver
   EndWith
   
   FreeStructure(*data)
EndProcedure

Procedure.i addThread(*block.Block, aufgabe.Aufgabe)
   Protected thread.i
   Protected *threadData.ThreadData = AllocateStructure(ThreadData)
   
   If Not *threadData
      ProcedureReturn #False
   EndIf
   
   With *block
      *threadData\block = *block
      *threadData\aufgabe = aufgabe
      If AddElement(\threads())
         \threads() = CreateThread(@AufgabeThread(), *threadData)
         If Not \threads()
            FreeStructure(*threadData)
            DeleteElement(\threads())
         EndIf
      Else
         FreeStructure(*threadData)
         ProcedureReturn #False
      EndIf
      
      \cThreads = ListSize(\threads())
   EndWith
   
   ProcedureReturn #True
EndProcedure

Procedure.i processBlock(*block.Block, *buffer, bufferSize.i, iBlock.i)
   Protected i.i
   
   With *block
      \buffer = *buffer
      \size = bufferSize
      \iBlock = iBlock
      
      ; Sage allen Threads, dass sie ihre Arbeit beginnen sollen
      For i = 1 To \cThreads
         SignalSemaphore(\waitSema)
      Next
      
      ; Warte, bis alle Threads fertig sind
      For i = 1 To \cThreads
         WaitSemaphore(\doneSema)
      Next
      
      ; Melde allen Threads, das nun alle fertig sind und auf neue Arbeit warten können.
      For i = 1 To \cThreads
         SignalSemaphore(\allDoneSema)
      Next
   EndWith
EndProcedure

Procedure.i destroyBlock(*block.Block)
  Protected i
   With *block
      \stop = #True
      
      ; Sage allen Threads, dass sie sich beenden sollen
      For i = 1 To \cThreads
         SignalSemaphore(\waitSema)
      Next
      
      ; Warte auf Beendigung der Threads
      ForEach \threads()
         WaitThread(\threads())
      Next
      
      FreeSemaphore(\waitSema)
      FreeSemaphore(\doneSema)
      FreeSemaphore(\allDoneSema)
   EndWith
   
   FreeStructure(*block)
EndProcedure

; ============== Hauptprogramm ===============

Procedure Aufgabe1(*Buffer, BufferSize, iBlock)
  ;Debug "Aufgabe1 bearbeitet Block: " + iBlock + " "
  AddFingerprintBuffer(0, *Buffer, BufferSize)
EndProcedure

Procedure Aufgabe2(*Buffer, BufferSize, iBlock)
  ;Debug "Aufgabe2 bearbeitet Block: " + iBlock
  AddFingerprintBuffer(1, *Buffer, BufferSize)
EndProcedure

Procedure Aufgabe3(*Buffer, BufferSize, iBlock)
  ;Debug "Aufgabe3 bearbeitet Block: " + iBlock
  AddFingerprintBuffer(2, *Buffer, BufferSize)
EndProcedure

Define.Block *block = newBlock()

StartFingerprint(0, #PB_Cipher_CRC32)
StartFingerprint(1, #PB_Cipher_MD5)
StartFingerprint(2, #PB_Cipher_SHA1)

addThread(*block, @Aufgabe1())
addThread(*block, @Aufgabe2())
addThread(*block, @Aufgabe3())

*buffer = AllocateMemory(#Buffersize)
If Not *buffer
   End
EndIf

CompilerSelect #PB_Compiler_OS
  CompilerCase #PB_OS_Windows
    sfile = #PB_Compiler_Home + "PureBasic.exe"
  CompilerDefault
    sfile = #PB_Compiler_Home + "compilers/PureBasic"  
CompilerEndSelect

If ReadFile(0, sfile)
   While Not Eof(0)
      iRead = ReadData(0, *buffer, #Buffersize)
      x + 1
      
      processBlock(*block, *buffer, iRead, x)
      
      ;Debug "============================"
      
   Wend
   CloseFile(0)
EndIf

FreeMemory(*buffer)

destroyBlock(*block)

Debug "Checksum of file: " + sfile + #CRLF$

CRC$ = FinishFingerprint(0)                  ; finish calculation
Debug "CRC32 checksum" + #TAB$ + " = " + CRC$

MD5$ = FinishFingerprint(1)                  ; finish calculation
Debug "MD5 checksum"   + #TAB$ + " = " + MD5$

SHA1$ = FinishFingerprint(2)                 ; finish calculation
Debug "SHA1 checksum"  + #TAB$ + " = " + SHA1$

PureBasic 5.46 LTS (Windows x86/x64) | windows 10 x64 Oktober failure
Benutzeravatar
NicTheQuick
Ein Admin
Beiträge: 8809
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

Re: Multi Threads

Beitrag von NicTheQuick »

Mit den Fingerprints habe ich das Teil auch getestet. :lol:

Geht aber echt gut. Dauert ca 17 Sekunden für eine 2,4 GB Datei, wobei ich noch SHA2 und SHA3 mit eingebaut habe. Wenn man die #BufferSize mal 1024 macht, bringt es auch noch einen guten Schub. Mit 8 kB Buffer dauert es fast doppelt so lange.

Code: Alles auswählen

EnableExplicit

Define *Buffer, iRead, x, sfile.s, CRC$, MD5$, SHA1$, SHA2$, SHA3$
#Buffersize = 8192 * 1024

Prototype Aufgabe(*buffer, bufferSize.i, iBlock.i)

UseCRC32Fingerprint()
UseMD5Fingerprint()
UseSHA1Fingerprint()
UseSHA2Fingerprint()
UseSHA3Fingerprint()


Structure Block
	*buffer       ; Pointer zum Puffer selbst
	size.i		  ; Größe des Puffers
	iBlock.i	  ; Nummer des Blocks
	waitSema.i	  ; Semaphore um auf Befüllen den Blockes zu warten
	doneSema.i	  ; Semaphore um auf Beendigung der Verarbeitung zu warten
	allDoneSema.i ;
	stop.i		  ; #True, wenn der Thread sich beenden soll
	List threads.i()
	cThreads.i    ; Anzahl der Threads
EndStructure

Procedure.i newBlock()
	Protected *block.Block = AllocateStructure(Block)
	
	With *block
		\waitSema = CreateSemaphore()
		\doneSema = CreateSemaphore()
		\allDoneSema = CreateSemaphore()
	EndWith
	
	ProcedureReturn *block
EndProcedure

Structure ThreadData
	*block.Block
	aufgabe.Aufgabe
EndStructure

Procedure AufgabeThread(*data.ThreadData)
	With *data\block
		Repeat
			; Warte bis ein neuer Block da ist
			WaitSemaphore(\waitSema)
			
			If \stop
				Break
			EndIf
			
			; Führe die Aufgabe auf dem Block aus
			*data\aufgabe(\buffer, \size, \iBlock)
			
			; Melde dem Hauptthread, dass du fertig bist
			SignalSemaphore(\doneSema)
			
			; Warte auf den Hauptthread, der dir mitteilt, das alle anderen Threads auch fertig sind
			WaitSemaphore(\allDoneSema)
		ForEver
	EndWith
	
	FreeStructure(*data)
EndProcedure

Procedure.i addThread(*block.Block, aufgabe.Aufgabe)
	Protected thread.i
	Protected *threadData.ThreadData = AllocateStructure(ThreadData)
	
	If Not *threadData
		ProcedureReturn #False
	EndIf
	
	With *block
		*threadData\block = *block
		*threadData\aufgabe = aufgabe
		If AddElement(\threads())
			\threads() = CreateThread(@AufgabeThread(), *threadData)
			If Not \threads()
				FreeStructure(*threadData)
				DeleteElement(\threads())
			EndIf
		Else
			FreeStructure(*threadData)
			ProcedureReturn #False
		EndIf
		
		\cThreads = ListSize(\threads())
	EndWith
	
	ProcedureReturn #True
EndProcedure

Procedure.i processBlock(*block.Block, *buffer, bufferSize.i, iBlock.i)
	Protected i.i
	
	With *block
		\buffer = *buffer
		\size = bufferSize
		\iBlock = iBlock
		
		; Sage allen Threads, dass sie ihre Arbeit beginnen sollen
		For i = 1 To \cThreads
			SignalSemaphore(\waitSema)
		Next
		
		; Warte, bis alle Threads fertig sind
		For i = 1 To \cThreads
			WaitSemaphore(\doneSema)
		Next
		
		; Melde allen Threads, das nun alle fertig sind und auf neue Arbeit warten können.
		For i = 1 To \cThreads
			SignalSemaphore(\allDoneSema)
		Next
	EndWith
EndProcedure

Procedure.i destroyBlock(*block.Block)
	Protected i
	With *block
		\stop = #True
		
		; Sage allen Threads, dass sie sich beenden sollen
		For i = 1 To \cThreads
			SignalSemaphore(\waitSema)
		Next
		
		; Warte auf Beendigung der Threads
		ForEach \threads()
			WaitThread(\threads())
		Next
		
		FreeSemaphore(\waitSema)
		FreeSemaphore(\doneSema)
		FreeSemaphore(\allDoneSema)
	EndWith
	
	FreeStructure(*block)
EndProcedure

; ============== Hauptprogramm ===============

Procedure HashCRC32(*Buffer, BufferSize, iBlock)
	AddFingerprintBuffer(0, *Buffer, BufferSize)
EndProcedure

Procedure HashMD5(*Buffer, BufferSize, iBlock)
	AddFingerprintBuffer(1, *Buffer, BufferSize)
EndProcedure

Procedure HashSHA1(*Buffer, BufferSize, iBlock)
	AddFingerprintBuffer(2, *Buffer, BufferSize)
EndProcedure

Procedure HashSHA2(*Buffer, BufferSize, iBlock)
	AddFingerprintBuffer(2, *Buffer, BufferSize)
EndProcedure

Procedure HashSHA3(*Buffer, BufferSize, iBlock)
	AddFingerprintBuffer(2, *Buffer, BufferSize)
EndProcedure

Define.Block *block = newBlock()

StartFingerprint(0, #PB_Cipher_CRC32)
StartFingerprint(1, #PB_Cipher_MD5)
StartFingerprint(2, #PB_Cipher_SHA1)
StartFingerprint(3, #PB_Cipher_SHA2, 512)
StartFingerprint(4, #PB_Cipher_SHA3, 512)

addThread(*block, @HashCRC32())
addThread(*block, @HashMD5())
addThread(*block, @HashSHA1())
addThread(*block, @HashSHA2())
addThread(*block, @HashSHA3())

*buffer = AllocateMemory(#Buffersize)
If Not *buffer
	End
EndIf

CompilerSelect #PB_Compiler_OS
	CompilerCase #PB_OS_Windows
		sfile = #PB_Compiler_Home + "PureBasic.exe"
	CompilerDefault
		sfile = #PB_Compiler_Home + "compilers/PureBasic" 
CompilerEndSelect

sfile = "/home/nicolas/Videos/MVI_7857.MOV"

Define time.i = ElapsedMilliseconds()

If ReadFile(0, sfile)
	While Not Eof(0)
		iRead = ReadData(0, *buffer, #Buffersize)
		x + 1
		
		processBlock(*block, *buffer, iRead, x)
		
	Wend
	CloseFile(0)
EndIf

FreeMemory(*buffer)

destroyBlock(*block)

CRC$ = FinishFingerprint(0)
MD5$ = FinishFingerprint(1)
SHA1$ = FinishFingerprint(2)
SHA2$ = FinishFingerprint(3)
SHA3$ = FinishFingerprint(4)

time = ElapsedMilliseconds() - time
Define.s result

result = "Checksum of file: " + sfile + #CRLF$ +
         "CRC32 checksum" + #TAB$ + " = " + CRC$ + #CRLF$ +
         "MD5 checksum"   + #TAB$ + " = " + MD5$ + #CRLF$ +
         "SHA1 checksum"  + #TAB$ + " = " + SHA1$ + #CRLF$ +
         "SHA2 checksum"  + #TAB$ + " = " + SHA2$ + #CRLF$ +
         "SHA3 checksum"  + #TAB$ + " = " + SHA3$ + #CRLF$ +
         "Time: " + StrD(time / 1000.0, 3) + " s"

SetClipboardText(result)

MessageRequester("Checksums", result)
Wer hat Lust auf einen Contest? Man müsste sich nur noch auf die Datengröße einigen, die wir dafür nutzen. :)
Benutzeravatar
xXRobo_CubeXx
Beiträge: 120
Registriert: 12.06.2015 16:08
Computerausstattung: Version 5.41 LTS
Wohnort: Wohnort
Kontaktdaten:

Re: Multi Threads

Beitrag von xXRobo_CubeXx »

Was ist der Unterschied zwischen CRC32Fingerprint, MD5Fingerprint und SHA1Fingerprint?
Nur andere Berechnungs Methoden? Gibt es auch Geschwindigkeitsunterschiede? Hat das einer getestet? Was ist das schnellste?
Danke fürs posten eurer Erfahrungen :allright: :)
Version 5.41 LTS :)
Benutzeravatar
NicTheQuick
Ein Admin
Beiträge: 8809
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

Re: Multi Threads

Beitrag von NicTheQuick »

CRC32, MD5, SHA1, SHA2 und SHA3 sind alles verschieden starke Prüfsummenverfahren oder auch Hashverfahren.
Üblicherweise speichert man zum Beispiel das Passwort eines Users immer als Hash in der Datenbank. Will dieser sich einloggen, wird die Eingabe gehasht und dann gegen den Hash in der Datenbank verglichen. Jetzt ist es aber so, dass CRC32, MD5 und SHA1 alleine nicht mehr zu gebrauchen sind, weil die geknackt wurden, d.h. es ist in absehbarer Zeit möglich ein Passwort zu finden, das den selben Hash ergibt. Bei SHA2 und SHA3 sieht das anders aus.
Normalerweise geht es hier auch nicht darum, was der schnellste Hash ist, sondern wie kryptographisch sicher der Hash ist.

Aber um deine Frage zu beantworten: CRC32 ist am schnellsten.
GPI
Beiträge: 1511
Registriert: 29.08.2004 13:18
Kontaktdaten:

Re: Multi Threads

Beitrag von GPI »

NicTheQuick hat geschrieben:Normalerweise geht es hier auch nicht darum, was der schnellste Hash ist, sondern wie kryptographisch sicher der Hash ist.

Aber um deine Frage zu beantworten: CRC32 ist am schnellsten.
es kommt halt darauf an, wozu du den Hash benutzen willst. Für Passwörter ist das klar. Wenn es aber bspw. darum geht, Übertragungsfehler rauszufinden, dürfte CRC32 deutlich praktischer sein.
CodeArchiv Rebirth: Deutsches Forum Github Hilfe ist immer gern gesehen!
Antworten