602SQL-Úplná dokumentace Index  

Vlákna a jejich synchronizace

Databázové aplikace mohou využívat souběžný běh více vláken řízení na serveru. Mohou spustit vlákna, která se provádějí nezávisle na příchodech požadavků od klientů, mohou jim předávat práci a přebírat výsledky.

Každý požadavek přicházející od klienta na server je zpracováván v samostatném vlákně. Pokud požadavek vyvolá provádění procedury uložené na serveru nebo triggeru, pak má možnost:

Spuštění dalšího vlákna

Další vlákno lze spustit provedením příkazu:

CALL DETACHED jméno_procedury( parametry ... )

Tento příkaz vytvoří nové, tzv. samostatné vlákno a spustí v něm zadanou proceduru se zadanými parametry. Volající vlákno pak pokračuje nezávisle dál, aniž by čekalo na dokončení této procedury. Jakmile takto zavolaná procedura skončí, je proveden commit transakce a její vlákno je zrušeno.

Samostatné vlákno vytvořené příkazem CALL DETACHED se přihlásí k serveru pod stejným uživatelským jménem, jako spouštějící vlákno, bude mít tedy stejný rozsah práv. Je-li samostatné vlákno spuštěné systémem, má administrátorská práva. Spuštěním nového vlákna se nespotřebovává žádná dodatečná licence.

Při běhu samostatného vlákna bude otevřena ta aplikace, v níž se nachází zavolaná procedura.

Procedura zavolaná v samostatném vlákně smí mít pouze vstupní parametry. Pokud by měla výstupní nebo vstupně-výstupní parametry, mohlo by dojít k pádu systému, pokud by skutečné parametry zanikly před dokončením procedury - kopírování hodnot do výstupních parametrů se totiž provádí až při ukončení procedury, po provedení posledního příkazu. Výsledky činnosti procedury se proto typicky zapisují do databáze.

Běžící samostatné vlákno blokuje jisté prostředky serveru (svými zámky, otevřenými kurzory apod.). Také brání provádění akcí, které vyžadují zamknutí serveru (oprava konzistence, zkompaktnění apod.).

Periodické provádění práce v samostatném vlákně

Pokud samostatné vlákno má periodicky provádět určitou práci, pak může využit volání standardní funkce Sleep. Tato funkce má jeden celočíselný parametr udávající počet milisekund. Funkce pozastaví provádění procedury, dokud neuplyne zadaný časový interval nebo dokud není ukončen SQL server. Pokud funkce skončí uplynutím intervalu, vrátí TRUE, jinak vrátí FALSE. Konstrukce, která zajistí periodické opakování určité práce každých 15 sekund, může vypadat takto:

REPEAT
   práce opakovaná s 15-sekundovými přestávkami
UNTIL NOT Sleep(15000)
END REPEAT;

Procedura skončí při zastavení SQL serveru.

Pokud se nějaká akce má provádět periodicky a kromě toho na externí pokyn, pak lze využít čekání na semafor s časovým limitem popsané níže. Čekání skončí, když vyprší zadaný limit nebo když jiné vlákno zvedne semafor.

Synchronizování vláken pomocí semaforů

Potřeba synchronizovat běh vláken vzniká například tehdy, pokud jedno vlákno připravuje práci pro jiné vlákno a chce jej probudit k činnosti nebo pokud naopak čeká na dokončení práce jiným vláknem. Synchronizovat lze kterékoli vlákno s kterýmkoli, bez ohledu ta to, zda běží jako samostatné nebo provádí požadavek klienta a bez ohledu na to, kterým klientem (resp. systémem) bylo spuštěno.

K synchronizaci vláken slouží semafory označené jmény. Vlákno, které chce pracovat se semaforem, zadá jeho jméno a obdrží celočíselný handle semaforu. Pokud dvě vlákna použijí stejné jméno semaforu, obdrží handle ke stejnému semaforu a mohou jej použít k synchronizaci.

Semafor se vytváří pomocí funkce Create_Semaphore, vstupní parametry určují jméno semaforu a pro kolik vláken bude po vytvoření otevřen. Semafor se ruší pomocí funkce Close_Semaphore, ke skutečnému zrušení semaforu však dojde až tehdy, když jej zruší všechna vlákna, která s nim pracují. Operaci zvednutí semaforu provádí funkce Release_Semaphore, atomickou operaci čekání na zvednutý semafor a následné spuštění semaforu provádí funkce Wait_For_Semaphore.

Příklad 1:

Nechť první vlákno připravuje práci pro druhé vlákno a čeká na její dokončení. První vlákno může vypadat takto:

DECLARE nova_prace, prace_hotova INT;
SET nova_prace = Create_Semaphore("nova prace", 0);
SET prace_hotova = Create_Semaphore("prace hotova", 0);
...  // příprava práce pro druhé vlákno
CALL Release_Semaphore(nova_prace);
CALL Wait_For_Semaphore(prace_hotova, -1);
... // práce je hotova, zpracování výsledků
CALL Close_Semaphore(nova_prace);
CALL Close_Semaphore(prace_hotova);

Druhé vlákno může vypadat takto:

DECLARE nova_prace, prace_hotova INT;
SET nova_prace = Create_Semaphore("nova prace", 0);
SET prace_hotova = Create_Semaphore("prace hotova", 0);
cykl: LOOP
  CALL Wait_For_Semaphore(nova_prace, -1);
  IF konec THEN LEAVE cykl;
  END IF;
  ... // provedení práce
  CALL Release_Semaphore(prace_hotova);
END LOOP;
CALL Close_Semaphore(nova_prace);
CALL Close_Semaphore(prace_hotova);

Činnost druhého vlákna se ukončí tak, že se nastaví podmínka konec a provede se příkaz:

CALL Release_Semaphore(nova_prace);

Tento model spolupráce dovoluje, aby druhé vlákno mělo větši rozsah práv, než první. Pokud první vlákno chce provést akce, k niž nemá dostatečná oprávnění, musí o ně požádat druhé vlákno.

Příklad 2:

Nechť existuje skupina příkazů, kterou smí v jednom okamžiku provádět nejvýše jedno vlákno (klient). Tento požadavek lze zajistit uzavřením příkazů to takovéto konstrukce:

DECLARE chranena_sekce INT;
// 1.  vytvoření semaforu pro vstup 1 vlákna 
//     opakované volání Create_Semaphore se stejným jménem v době, kdy existuje, vrací stejný handle
SET chranena_sekce = Create_Semaphore("chráněná sekce", 1);
// 2. čekání na semafor: "je-li dole, čekej (-1=neomezeně), je-li nahoře, tak jdi dál a dej semafor dolu"
CALL Wait_For_Semaphore(chráněná_sekce, -1);
  ... // příkazy (ukončené commitem, aby zámky neblokovaly práci dalšího klienta)
// 3. zvednout semafor, aby další mohl dovnitř
CALL Release_Semaphore(chranena_sekce);
// 4. zrušit semafor (skutečně se zruší až tehdy, kdy jej nevyužívá žádné vlákno)
CALL Close_Semaphore(chranena_sekce);

Pokud vlákno opomene zrušit semafor, SQL server jej zruší automaticky při ukončení vlákna.

Spolupráce vláken a transakce

Pokud jedno vlákno provede změny v databázi a chce, aby změněná data byla viditelné pro jiná vlákna, musí provést explicitní COMMIT. Jinak, v závislosti na nastaveném stupni izolace transakcí, budou ostatní vlákna buď vidět dosud nezměněná data nebo budou pozastavena do dokončení transakce prvním vláknem.

Je-li nastaven stupeň izolace SERIALIZABLE, pak pokud vlákno přečetlo jistá data a chce umožnit jiným vláknům tato data přepsat, musí provést explicitní COMMIT bez ohledu na to, kým bylo spuštěno.

Informace o vláknech

Veškeré informace o spuštěných vláknech (spuštěných explicitně nebo systémem) získáte pomocí systémového dotazu _iv_logged_users ve tvaru

SELECT * FROM _iv_logged_users
WHERE detached OR worker_thread
nebo v okně Monitor serveru. O řízení běhu vláken (včetně předčasného ukončení) se dočtete zde.