Dále v konfigurátoru "Typ spouštění položek" lze nastavit smysl timeoutu, který se zadává v metodě Add. Lze určit, zda požadovaný timeout je doba mezi zařazaním položky a jejím spuštěním (tzn. Položky se spouští nezávisle) nebo timeout je prodleva mezi spuštěním této položky a předchozí spuštěné položky (tzn. Položky se spouští postupně).
Položku čekající ve frontě lze předčasně ukončit (tzn. dřív než vyprší její čas) metodou Release nebo Remove:
V události onStep lze tehdy nastavit pEvent.ReleaseCancel=true a tím odstranění z fronty zrušit - tak je možné jednu položku fronty volat vícenásobně.
Upozornění! Pokud se použije jiné vlákno (thread), pak přístup k ostatním objektům ve skriptu probíhá v jiném než hlavním vlákně a je nutno vzít v úvahu případné problémy se synchronizací při čtení a zápisu do Pma objektů. Tato volba je vhodná zejména pro časově náročné výpočetní úkony na pozadí.
Jedním ze základních způsobů je rozdělení celé činnosti na 3 kroky. A to čtení dat z aplikace, zpracování dat a zápis výsledných dat do aplikace (každý krok je implementován vyvoláním události onStep). Krok čtení a krok zápisu pak lze nechat vyvolat v hlavním vlákně (nedojde k problémům se synchronizací dat) a krok zpracování dat pak lze nechat vyvolat v pracovním vlákně. Určení, zda má být krok proveden v hlavním nebo pracovním vlákně lze určit v parametru Params metody PmaSequencer.Add. Čtení dat z aplikace a zápis dat zpět do aplikace pak probíhají v hlavním vlákně a vlastní zpracování dat pak probíhá v samostatném pracovním vlákně a nezdržuje hlavní vlákno. Jednotlivé kroky si musí předávat vyčtená a zpracovávaná data. Nejjednodušší k předání je vytvořit ve skriptu daného kroku (událost onStep) pomocné pole a předat je jako parametr Val2 metody PmaSequencer.Add. Existuje samozřejmě také možnost předávat si data v objektu PmaData. Použití viz Příklad časově náročného zpracování v pracovním vlákně.
Je požadavek provést následující algoritmus:
Toto lze pomocí objektu PmaSequencer provést následovně:
oSequencer.Add(t1, "A1");
Po t1 sekundách je vyvolána událost onStep, ve které provedeme akci A1 a poté zařadíme nový časový požadavek pro akci A2. To znamená, že v události onStep může být například následující skript:
switch (pEvent.Val1)
{
case "A1":
//algoritmus akce A1
pMe.Add(t2, "A2");
break;
case "A2":
//algoritmus akce A2
pMe.Add(t3, "A3");
break;
case "A3":
//...
break;
}
Takovým způsobem můžeme tvořit libovolně dlouhou sekvenční posloupnost akcí, která může být i podmíněná (např. za určitých podmínek se po akci A1 neprovede akce A2 ale hned akce A3). Výhoda je, že všechny akce máme v jednom skriptu (v události onStep) a je tedy přehledně vidět celá sekvenční posloupnost. V přechodech mezi akcemi lze uchovávat vnitřní stavy do proměnných Val2 a Val3 (v příkladu jsou nevyužity).
Je to podobné jako v předchozím případě, vytvoříme to však nyní s typem spouštění: 1 = položky se spouští postupně, timeout je prodleva mezi nimi.
Zavoláme Add pro všechny položky najednou:
oSequencer.Add(5, "A1");
oSequencer.Add(5, "A2");
oSequencer.Add(5, "A3");
oSequencer.Add(5, "A4");
a v události onStep pak může být jednodušší skript:
switch (pEvent.Val1)
{
case "A1":
//algoritmus akce A1
break;
case "A2":
//algoritmus akce A2
break;
case "A3":
//...
break;
}
V tom případě se 1.akce A1 spustí okamžitě (nebo 5 sekund po spuštění aplikace), protože předchozí položka neexistovala a čas spuštění předchozí položky je tudíž nastaven na čas spuštění runtime. Další položky se však spouští v 5 sekundových intervalech po sobě.
Typ spouštění bude: 1 = položky se spouští postupně, timeout je prodleva mezi nimi.
Zavoláme metodu Add pro obě položky najednou:
a v události onStep pak může být jednodušší skript:
switch (pEvent.Val1)
{
case "A1":
//algoritmus akce A1
pEvent.ReleaseCancel = true;
break;
case "A2":
//algoritmus akce A2
break;
}
V tom případě se 1. akce A1 spustí okamžitě. Další položkaka se spustí za 10 sekund, ale může být spuštěna i dříve, pokud je po úspěšném ukončení první akce volána metoda Release:
//algoritmus akce A1
oSeq.Release(10, 0);
V události onStep pak může být například následující skript:
switch (pEvent.Val1)
{
case "A1":
//algoritmus akce A1
break;
case "A2":
//algoritmus akce A2
break;
}
Je to podobné sekvenčnímu řízení s tím rozdílem, že akce na sobě nejsou závislé ale každá se provede samostatně. Opět v příkladě nejsou využity proměnné Val2 a Val3, které by šly použít pro dodatečné parametry akce.
Typ spouštění bude 0 = položky se spouští nezávisle, každá podle svého timeoutu a do objektu PmaSequencer zařadíme položku pro akci A1:
oSequencer.Add(t1, "A1", 111);
Tímto se do objektu zařadí položka, jejíž proměnná Val1 bude text "A1" a proměnná Val2 bude číslo 111.
Do skriptu asynchronní události od konce přenosu (po stisku tlačítka ...) vložíme následující příkaz:
oSequencer.Release(1, 2, "A1", 222);
Tento příkaz znamená, že se má v daném objektu PmaSequencer uvolnit položka, jejíž proměnná Val1 (=parametr 1) je rovna textu "A1" a při tomto uvolňování se této položce má přiřadit do proměnné Val2 (= parametr 2) číslo 222 (které přepíše původně nastavené číslo 111).
V události onStep pak může být například následující skript:
switch (pEvent.Val2)
{
case 111:
//algoritmus akce A1 Pokud vypršel timeout
break;
case 222:
//algoritmus akce A1 Pokud nastala asynchronní událost
break;
}
V tomto příkladu jsme v podstatě ani nepotřebovali indikaci o akci A1 (tzn. text "A1" v proměnné Val1). Příklad však lze zobecnit tak, že daný objekt PmaSequencer bude umět zpracovávat více asynchronních akcí a pak bychom v události onStep ještě museli rozlišovat podle hodnot proměnné Val1.
oSequencer.Add(0, "read", 0, 0, "thread:main;");
Událost onStep pro všechny 3 kroky má následující tvar:
switch (pEvent.Val1)
{
case "read": //vykoná se v hlavním threadu main
//příprava a vytvoření pole souborů aDataRead ke zpracování v dalšímu kroku
var aDataRead = Pm.CreatePmArray().Array1("Glob.ini", "Cfg1.ini", "Cfg2.ini", "Cfg3.ini");
pMe.Add(0, "work", aDataRead, 0, "thread:work;");
break;
case "work": //vykoná se v pracovním threadu work
//zpracovávání vstupních dat v pEvent.Val2 na výstupní data v poli aDataWrite a předání výstupních dat dalšímu kroku
var aFiles = pEvent.Val2;
var nFiles = aFiles.GetSize(1);
var aDataWrite = Pm.CreatePmArray(nFiles);
var i;
for (i = 0; i < nFiles; i++)
aDataWrite.SetItem(Pm.IniFileRead("#cfg:" + aFiles.GetItem(i), "MySettings", "value", 0, 4), i);
pMe.Add(0, "write", aDataWrite, 0, "thread:main;");
break;
case "write": //vykoná se v hlavním threadu main
//zápis výstupních dat v pEvent.Val2 do aplikace
var aValues = pEvent.Val2;
var val1 = aValues.GetItem(0);
//...
break;
}
Čtení i zápis dat aplikace má být provedeno bezpečným způsobem z hlediska synchronizace dat (použít hlavní vlákno), zatímco náročné zpracování dat má být provedeno na pozadí v jiném vlákně (optimalizace aplikace). V kartě "Sequencer" objektu zvolíme Typ spouštění položek=1 = položky se spouští postupně, timeout je prodleva mezi nimi, dále zvolíme Použité vlákno (thread)=Normal = nové pracovní vlákno normální priority. Požadavek na první krok (hlavní vlákno) se vytvoří zavoláním metody Add s identifikací kroku "read" v parametru Val1.
Takovým způsobem můžeme tvořit libovolně dlouhou sekvenční posloupnost kroků, kde jednotlivé kroky mohou být vyvolávany jak v hlavním, tak pracovním vlákně. Výhoda je, že všechny kroky máme v jednom skriptu (v události onStep) a je tedy přehledně vidět celá sekvenční posloupnost. V přechodech mezi kroky lze uchovávat vnitřní stavy do proměnných Val2 a Val3.