Multi Autoit - Threading in Autoit

  • Ich weiß es gibt schon Multithreading UDF('s), aber ich glaube die waren schwerer zu benutzen (aber dafür vielleicht besser).
    Die Idee habe ich von einem Beitrag von ProgAndy, den ich nicht mehr wiederfinde :D
    Aber ich habe noch sein Script:

    Spoiler anzeigen
    [autoit]


    $handle = DllCallbackRegister("_Threadstart","int","ptr")
    ; Struct to send to the Thread
    $dll = DllStructCreate("Char[200];int")
    DllStructSetData($dll,1,"hi")
    DllStructSetData($dll,2,1234)
    $ret = DllCall("kernel32.dll","hwnd","CreateThread","ptr",0,"dword",0,"long",DllCallbackGetPtr($handle),"ptr",DllStructGetPtr($dll),"long",0,"int*",0)
    MsgBox(0, "Thread-Info", "Handle: " & $ret[0] & @CRLF & "Thread-ID: " & $ret[6])
    Func _ThreadStart($x)
    $y = DllStructCreate("char[200];int",$x)
    MsgBox(0, "Thread 1", DllStructGetData($y,1) & @CRLF & DllStructGetData($y,2)&@CRLF &"AutoIt can Multithreading !!!")
    EndFunc

    [/autoit]

    So darauf habe ich jetzt eine UDF gemacht, nicht groß oder sondervoll wenn man das Script gesehen hat, aber ich denke trotzdem nützlich

    Script
    [autoit]


    ;Beispiel:
    Global $th=_ThreadCreate("_TS","char[2];int","hi;1")
    ;~ _ThreadClose($th);*
    MsgBox(0,"","")

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    Func _TS($x)
    Sleep(10) ;Damit $th schon zugewiesen ist, wenn die Daten ausgelesen werden
    ;~ Sleep(1000);*
    Local $y=_ThreadDataRead($x,$th[2]) ;Die Daten könnte man natürlich auch manuel auslesen
    if IsArray($y) Then
    MsgBox(0,$y[0],$y[1])
    Else
    MsgBox(0,"NoData","Tja, Pech gehabt!")
    EndIf
    EndFunc

    [/autoit] [autoit][/autoit] [autoit]

    ;* Beispiel für Threadclose, wenn nur eine Msgbox angezeigt wird, hat es gekklappt

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    ;UDF:
    Func _ThreadCreate($func,$globalstruct="",$zuweisungen="") ;$func must have one Parameter, Data can be read with _ThreadDataRead
    Local $threadinfo[3],$ptr
    If $globalstruct="" Then
    $ptr=0
    Else
    $struct=DllStructCreate($globalstruct)
    If $zuweisungen<>"" Then
    Local $split=StringSplit($zuweisungen,";")
    If @error Then
    Local $split[2]
    $split[0]=1
    $split[1]=$zuweisungen
    EndIf
    For $x=1 To $split[0]
    DllStructSetData($struct,$x,$split[$x])
    Next
    EndIf
    $ptr=DllStructGetPtr($struct)
    EndIf
    Local $cbr=DllCallbackRegister($func,"int","ptr")
    Local $ret=DllCall("kernel32.dll","hwnd","CreateThread","ptr",0,"dword",0,"long",DllCallbackGetPtr($cbr),"ptr",$ptr,"long",0,"int*",0)
    If Not IsArray($ret) Or @error Then Return SetError(@error,@extended,0)
    $threadinfo[0]=$ret[0];Handle
    $threadinfo[1]=$ret[6];Thread-ID
    $threadinfo[2]=$globalstruct;information struct
    Return $threadinfo
    EndFunc

    [/autoit] [autoit][/autoit] [autoit]

    Func _ThreadDataRead($param,$tgis);Returns Data in Array
    If $tgis="" Then Return 0
    $struct=DllStructCreate($tgis,$param)
    Local $split=StringSplit($tgis,";")
    If @error Then
    Local $split[2]
    $split[0]=1
    EndIf
    Local $ret[$split[0]]
    For $x=1 To $split[0]
    $ret[$x-1]=DllStructGetData($struct,$x)
    Next
    Return $ret
    EndFunc

    [/autoit] [autoit][/autoit] [autoit]

    Func _ThreadClose(ByRef $threadinfo,$exitcode=0)
    If Not IsArray($threadinfo) Then Return SetError(1,0,-1)
    DllCall("kernel32.dll","bool","TerminateThread","handle",$threadinfo[0],"dword",$exitcode)
    DllCall("kernel32.dll","int","CloseHandle","handle",$threadinfo[0])
    EndFunc

    [/autoit]

    So und jetzt setze ich mich warscheinlich wieder an etwas anderes :D


    PS:
    Weiß jemand ob ein Thread die Ausführung aufteilt (also nicht insgesamt schneller ist) oder kann man sie gebrauchen um ein Script schneller zu machen?

    EDIT:
    Ich weiß nicht warum, aber irgendwie kommen nach Phasen Fehler und dann wieder nicht, obwohl es das gleiche Script ist

    Einmal editiert, zuletzt von TheShadowAE (8. Juni 2010 um 15:24)

  • Weiß jemand ob ein Thread die Ausführung aufteilt (also nicht insgesamt schneller ist) oder kann man sie gebrauchen um ein Script schneller zu machen?


    Schöne UDF erstmal. Ich kann nur allgemein sagen: Wenn du es schaffst, einen Thread auf einen anderen Prozessorkern zu legen, ist das Skript im Idealfall doppelt so schnell.

    Twitter: @L3viathan2142
    Benutze AutoIt persönlich nicht mehr, da ich keinen Windows-Rechner mehr besitze.

  • Weiß jemand ob ein Thread die Ausführung aufteilt (also nicht insgesamt schneller ist) oder kann man sie gebrauchen um ein Script schneller zu machen?


    Vielleicht versteh ich noch nicht ganz was du meinst.
    Ein Thread kann nur auf einem Prozessor (Kern) laufen.
    Bei einem Quadcore (Hyperthreading lass ich mal außen vor) würde man bei einen Singlethreadprogramm also nur 1/4 der maximalen Leistung nutzen können.
    Beim Multithreading bietet sich daher an Performance-kritische Bereiche auf Threads zu verteilen um so auch die anderen Prozessorkerne zu nutzen.
    Idealerweise bieten sich da Schleifen an.
    Fast alle C++ Threading-Bibliotheken (Boost, OpenMP, TBB) bieten dort spezielle Forschleifenkonstrukte an mit denen eine Schleife in parallele Teile aufgeteilt wird.
    Stellen wir uns das mal in AutoIt-Syntax vor:
    Wir haben folgende Schleife:

    Spoiler anzeigen
    [autoit]

    For $i = 1 To 1000
    ConsoleWrite($i * 2 & @CRLF)
    Next

    [/autoit]


    Auf einem Quadcore würde sich anbieten die Schleife folgendermaßen aufzuteilen (und so wird es tatsächlich auch gemacht):

    Spoiler anzeigen
    [autoit]

    For $i = 1 To 250
    ConsoleWrite($i * 2 & @CRLF)
    Next

    [/autoit] [autoit][/autoit] [autoit]

    For $i = 251 To 500
    ConsoleWrite($i * 2 & @CRLF)
    Next

    [/autoit] [autoit][/autoit] [autoit]

    For $i = 501 To 750
    ConsoleWrite($i * 2 & @CRLF)
    Next

    [/autoit] [autoit][/autoit] [autoit]

    For $i = 751 To 1000
    ConsoleWrite($i * 2 & @CRLF)
    Next

    [/autoit]


    Nur das diese Schleifen dann halt gleichzeitig ausgeführt werden.
    Wenn man den entstehenden Overhead mal vernachlässigt sollte die Schleife im Idealfall in nur 1/4 der Zeit abgearbeitet sein.
    Also ja - Threading kann dort Performance bringen wenn eigentlich sequentielle Berechnungen auch parallel ausgeführt werden können.
    Mal von den Performancemöglichkeiten abgesehen schafft es auch weitere Möglichkeiten generell in der Programmierung da der Begriff der zeitgleichen Ausführung mit Threading überhaupt erst einmal möglich wird.

    Einmal editiert, zuletzt von AspirinJunkie (2. Juli 2010 um 16:24)

  • Super :thumbup:

    //Edit: Stürzt ab

    Windows 7 x86


    Wie gesagt, leider stürzt es immer wieder ohn bekannten Grund ab oder hat andere Fehler


    EDIT:
    So gibt es nie ein Fehler bei mir und startet Synchron die Msgbox:

    Spoiler anzeigen
    [autoit]


    Global $th=_ThreadCreate("_TS")
    Sleep(1)
    MsgBox(0,"","")

    [/autoit] [autoit][/autoit] [autoit]

    Func _TS($x)
    Sleep(1)
    MsgBox(0,"NoData","Tja, Pech gehabt!")
    EndFunc

    [/autoit]

    So funktioniert es aber leider manchmal wieder nicht:

    Spoiler anzeigen
    [autoit]


    #include <Misc.au3>
    Global $th=_ThreadCreate("_TS")
    Sleep(1)
    MsgBox(0,"","")

    [/autoit] [autoit][/autoit] [autoit]

    Func _TS($x)
    While Not _IsPressed("1")
    sleep(10)
    Wend
    Sleep(1)
    MsgBox(0,"NoData","Tja, Pech gehabt!")
    EndFunc

    [/autoit]

    Übrigens XP x86


    EDIT:
    AspirinJunkie
    Ist bei einem nicht Duo/Quad Prozessor dann nicht schneller?
    Oder ist es bei allen Prozessoren schneller?

    3 Mal editiert, zuletzt von TheShadowAE (8. Juni 2010 um 16:01)

  • Zitat

    AspirinJunkie
    Ist bei einem nicht Duo/Quad Prozessor dann nicht schneller?
    Oder ist es bei allen Prozessoren schneller?

    Performancevorteile sollte man da nicht mehr haben (mögliche Ausnahme: Hyperthreading-Prozessoren)
    Du kannst aber immer noch mehrere Codeteile quasi "zur gleichen Zeit" ausführen lassen wobei das hier dann bisschen aufgeweicht wird da der Prozessor nur ganz schnell zwischen den Threads hin und herspringt und es dann eine richtige Zeitgleichheit reell nicht gibt.

  • Diese Möglichkeit des Multithreadings ist aus gutem Grund in der Versenkung verschwunden. Der AutoIt-Interpreter ist nicht threadsicher. Das bedeutet, dass er nicht mehrere Codeblöcke gleichzeitig ausführen kann, da interene Variablen und Speicherbereiche nicht für gleichzeitige Zugriffe ausgelegt sind. Dadurch gibt es unvorhersehbare Interferenzen der Threads, die meist aufgrund von Speicherfehlern oder öhnlichem zum Absurz führen. Weiterhin beendet der AutoIt-Compiler das Programm sobald der erste Thread beendet wird.

    Daher ist es keine gute Idee, diese Technik einzusetzen. Will man jedoch nicht blockierende MsgBoxen erzeigen, lässt sich CreateThread sehr gut verwenden. Dazu habe ich die meisten Threadfuntkionen in UDFs gekapselt und hier mit Beispiel veröffentlicht: http://www.autoitscript.com/forum/index.php?showtopic=25494&view=findpost&p=642848
    Weiterhin lässt sich ASM-ode als Thread ausführen oder die Funktion aus einer DLL.
    Niemals darf jedoch DLLCallbackRegister als Thread verwendet werden.

  • Ja, du kannst eine DLL erstellen, die eine Funktion mit Schleife enthält. Wenn du diese mehrmals starten willst, musst du darauf achten, dass der Zugriff globale Variablen gesichert wird.

  • Wenn du die Threadhandles in einem Array verwalten willst, ist es vermutlich einfacher, das in AutoIt zu tun.