IR
irwinrodriguez.dev
Zurück zur Dokumentation

Beispiele

Drei vollständige Programme, genau so, wie sie laufen. Sie sind aus den Beispielen im Paket von FoxCore.Runtime 3.4.2 adaptiert, aus dem Ordner ejemplos\: kopieren, in eine .prg einfügen und aus der Visual-FoxPro-IDE ausführen.

Geandert wurde gegenuber den Paketversionen nur, woher die Lizenz kommt. Die .prg im Paket lesen den JWT aus einer Datei auf der Platte, weil sie einzelne Dateien sind, die man von Hand startet und ihn irgendwoher nehmen muss; hier steht er in einer Konstanten, so wie es eine echte Anwendung macht. Siehe Lizenzierung.

1. Eine lange Aufgabe, die sagt, wo sie steht

Der Worker ruft runtime.Progress() auf, und hier wird es auf beide Arten abgeholt: durch Fragen mit GetProgress() und durch Benachrichtigung über einen Sink. Die zweite ist die, die man verstanden haben sollte.

EcoDeProgreso: der Fortschritt einer Aufgabe, abgefragt und über einen Sink
LOCAL loRt, loEco, lcId, lcScript, lcJwt

lcJwt = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...."   && hier den JWT aus Ihrem Kundenportal einfügen

loRt = CREATEOBJECT("FoxCore.Runtime")
IF !loRt.Activate(lcJwt)
    MESSAGEBOX("La licencia de FoxCore no es valida.", 16, "FoxCore")
    RETURN
ENDIF
loRt.Attach(_VFP)

loEco = CREATEOBJECT("EcoDeProgreso")
? "sink aceptado :", loRt.SetEventSink(loEco)

TEXT TO lcScript NOSHOW
FOR i = 1 TO 5
    runtime.Progress(i * 20, 'paso ' + TRANSFORM(i) + ' de 5')
    =INKEY(0.2)
ENDFOR
runtime.Log('info', 'terminado sin novedad')
RETURN 'listo'
ENDTEXT

lcId = loRt.RunScript(lcScript, "con progreso")
DO WHILE !loRt.IsCompleted(lcId)
    loRt.Pump()                             && der Sink wird VON HIER aufgerufen
ENDDO
loRt.Pump(200, 1000)                        && eine letzte Runde: es können Meldungen in der Warteschlange sein

? "avisos recibidos :", loEco.nAvisos
? "ultimo aviso     :", loEco.cUltimo
? "ultimo log       :", loEco.cLog
? "y sin sink       :", loRt.GetProgress(lcId), loRt.GetProgressMessage(lcId)

loRt.ClearEventSink()                       && wichtig: solange sie ihn hält, lebt Ihr Objekt
loRt.Shutdown()
RETURN


DEFINE CLASS EcoDeProgreso AS Custom
    nAvisos = 0
    cUltimo = ""
    cLog    = ""

    *-- OnProgress y OnLog son OBLIGATORIOS: sin los dos, SetEventSink da .F.
    PROCEDURE OnProgress(tcTaskId, tnPercent, tcMessage)
        THIS.nAvisos = THIS.nAvisos + 1
        THIS.cUltimo = TRANSFORM(tnPercent) + "% " + TRANSFORM(tcMessage)
    ENDPROC

    PROCEDURE OnLog(tcTaskId, tcLevel, tcMessage)
        THIS.cLog = TRANSFORM(tcLevel) + ": " + TRANSFORM(tcMessage)
    ENDPROC

    *-- Este es opcional: se detecta si esta, y si no, no pasa nada.
    PROCEDURE OnTaskCompleted(tcTaskId, tcState)
    ENDPROC
ENDDEFINE
Der Sink wird NICHT von einem .NET-Thread aufgerufen: ihn ruft Pump() auf, in IHREM Thread. Deshalb darf eine Sink-Methode den Bildschirm anfassen. Und das Pump(200, 1000) am Ende ist keine Zierde: wenn die Aufgabe endet, können noch Meldungen in der Warteschlange stehen, und ohne diese letzte Runde werden sie nicht zugestellt.

Was Sie sehen werden

sink aceptado : .T.
avisos recibidos :          5
ultimo aviso     : 100% paso 5 de 5
ultimo log       : info: terminado sin novedad
y sin sink       : 100,00 paso 5 de 5

2. Eine Tabelle auf mehrere Worker verteilen

Das meistgefragte Rezept, und das am häufigsten falsch gemachte. Entscheidend ist: nicht der Cursor wird verteilt, sondern der BEREICH. Jeder Worker öffnet die Tabelle selbst und bearbeitet einen Abschnitt, also ist nichts geteilt und es braucht keine Sperren.

Eine Tabelle in Abschnitten auf vier Worker verteilt summieren
LOCAL loRt, lcDbf, i, lnDesde, lnHasta, lnPorTramo, lnTotal, lnHechas, lcTerm, lcJwt
LOCAL ARRAY laIds[4], laTerm[1]

lcJwt = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...."   && hier den JWT aus Ihrem Kundenportal einfügen

*-- Una tabla de ejemplo con 400 lineas
CREATE CURSOR ventas (id I, importe Y)
FOR i = 1 TO 400
    INSERT INTO ventas VALUES (i, i * 1.25)
ENDFOR
lcDbf = ADDBS(SYS(2023)) + "r09_ventas.dbf"
COPY TO (lcDbf)
USE IN SELECT("ventas")

loRt = CREATEOBJECT("FoxCore.Runtime")
IF !loRt.Activate(lcJwt)
    MESSAGEBOX("La licencia de FoxCore no es valida.", 16, "FoxCore")
    RETURN
ENDIF
loRt.Attach(_VFP)
loRt.MaxWorkers = 4

*-- Cuatro tramos de 100. jeder Worker öffnet die Tabelle SELBST y suma el suyo.
lnPorTramo = 100
FOR i = 1 TO 4
    lnDesde = (i - 1) * lnPorTramo + 1
    lnHasta = i * lnPorTramo
    laIds[i] = loRt.RunScript( ;
        "USE '" + lcDbf + "' IN 0 SHARED ALIAS ventas" + CHR(13) + CHR(10) + ;
        "SELECT ventas" + CHR(13) + CHR(10) + ;
        "SUM importe TO lnT FOR BETWEEN(id, " + TRANSFORM(lnDesde) + ", " + TRANSFORM(lnHasta) + ")" + CHR(13) + CHR(10) + ;
        "USE IN ventas" + CHR(13) + CHR(10) + ;
        "RETURN lnT", "tramo " + TRANSFORM(i))
ENDFOR

lnHechas = 0
DO WHILE lnHechas < 4
    loRt.Pump()
    lcTerm = loRt.PollCompleted()
    IF !EMPTY(lcTerm)
        lnHechas = lnHechas + ALINES(laTerm, lcTerm, 1, ";")
    ENDIF
ENDDO

*-- und zusammengefuhrt wird hier, wo es einen einzigen Thread gibt
lnTotal = 0
FOR i = 1 TO 4
    ? "  tramo " + TRANSFORM(i) + " :", loRt.GetState(laIds[i]), loRt.GetResult(laIds[i])
    IF loRt.GetState(laIds[i]) == "Completed"
        lnTotal = lnTotal + loRt.GetResult(laIds[i])
    ENDIF
ENDFOR
? "total en paralelo :", lnTotal, "  (esperado 100250,00)"

ERASE (lcDbf)
loRt.Shutdown()
RETURN
Die Schleife nützt PollCompleted(), das in EINEM Aufruf die seit dem letzten Mal beendeten IDs liefert. Bei vier Abschnitten macht das keinen Unterschied, bei Hunderten ist es der Unterschied zwischen einem COM-Aufruf pro Runde und N. Und summiert wird im Host, wo es einen einzigen Thread und kein Problem gibt.

Was Sie sehen werden

  tramo 1 : Completed       6312,50
  tramo 2 : Completed      18812,50
  tramo 3 : Completed      31312,50
  tramo 4 : Completed      43812,50
total en paralelo :     100250,00   (esperado 100250,00)

3. Ein eigener Handler, vom Worker aufgerufen

Der klassische Fall: eine laufende Nummer. Acht Prozesse fragen gleichzeitig nach einer Nummer und keine einzige ist doppelt, mit einem Objekt, das keinen einzigen Lock hat.

CajaDeFolios: eine laufende Nummer, vom Host vergeben
LOCAL loRt, loCaja, i, lnHechas, lcTerminadas, lcJwt
LOCAL ARRAY laIds[8], laTerm[1]

lcJwt = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...."   && hier den JWT aus Ihrem Kundenportal einfügen

loRt = CREATEOBJECT("FoxCore.Runtime")
IF !loRt.Activate(lcJwt)
    MESSAGEBOX("La licencia de FoxCore no es valida.", 16, "FoxCore")
    RETURN
ENDIF
loRt.Attach(_VFP)
loRt.MaxWorkers = 4

loCaja = CREATEOBJECT("CajaDeFolios")
? "handler aceptado :", loRt.RegisterHandler("SiguienteFolio", loCaja, "Siguiente")

FOR i = 1 TO 8
    laIds[i] = loRt.RunScript("RETURN runtime.Call('SiguienteFolio', 'FRA')", "folio " + TRANSFORM(i))
ENDFOR

lnHechas = 0
DO WHILE lnHechas < 8
    loRt.Pump()                             && hier werden die Aufrufe bedient
    lcTerminadas = loRt.PollCompleted()
    IF !EMPTY(lcTerminadas)
        lnHechas = lnHechas + ALINES(laTerm, lcTerminadas, 1, ";")
    ENDIF
ENDDO

? "folios emitidos :", loCaja.nUltimo
FOR i = 1 TO 8
    ?? " " + TRANSFORM(loRt.GetResult(laIds[i]))
ENDFOR

loRt.Shutdown()
RETURN


DEFINE CLASS CajaDeFolios AS Custom
    nUltimo = 0

    *-- ohne einen einzigen Lock, und es braucht keinen: esto se ejecuta en el hilo del host,
    *-- de una llamada en una llamada.
    FUNCTION Siguiente(tcSerie)
        THIS.nUltimo = THIS.nUltimo + 1
    RETURN TRANSFORM(tcSerie) + "-" + PADL(THIS.nUltimo, 4, "0")
    ENDFUNC
ENDDEFINE
Weil der Host sie einen nach dem anderen bedient, innerhalb von Pump(), in Ihrem Thread. Die Serialisierung kommt vom Pump, nicht vom Objekt. runtime.Call() ist synchron: es blockiert den Worker, bis Ihr Pump() es bedient, und wer nicht pumpt, läuft in die Frist und das Skript bekommt einen lesbaren Fehler.

Was Sie sehen werden

handler aceptado : .T.
folios emitidos :          8 FRA-0001 FRA-0002 FRA-0003 FRA-0004 FRA-0005 FRA-0006 FRA-0007 FRA-0008
Kommen Sie von der klassischen Engine? Diese Beispiele verwenden weder FoxCoreClass noch Run() oder WaitFor(). Die Entsprechungstabelle steht in "Was es nicht mehr gibt", am Ende der API-Referenz.

Weiter: API-Referenz ->