Windows - Szinkron és aszinkron hívások

forráskód letöltése
A Windows Management Instrumentation (WMI) szolgáltatás eljárásait, függvényeit több módon is meghívhatjuk. Cikkünkben a szinkron és aszinkron hívásokat hasonlítjuk össze. Sorozatunk eddigi részeiben nagyrészt a szinkron típust alkalmaztuk, ezért most a processzor időt hatékonyabban használó aszinkronra helyezzük a hangsúlyt. Szokásunkhoz híven tesszük mindezt gyakorlati példákon keresztül bemutatva.
Cikkünkhöz mellékelve megtalálható az alábbiakban szereplő szkript. Futtatva aszinkron módszerrel lekérdezi és megjeleníti a rendszerben futó folyamatok neveit.
Az első és legalapvetőbb különbség egy szinkron és egy aszinkron függvényhívás között az, hogy a szinkron csak addig képes az eredményeket fogadni, amíg tart a lekérdezés. Tehát ha a hívásának pillanatában rendelkezésre áll az információ, akkor sikeres lesz a lekérdezés, ha nem, akkor nem. Az aszinkron hívásoknál nem ez a helyzet. Nem kell a hívás pillanatában rendelkezésre állnia az eredménynek, később is megérkezhet és ha megérkezik sikeres lesz a lekérdezés. Egy egyszerű példán keresztül talán érthetőbb: készítünk egy függvényhívást, amely azt vizsgálja, hogy történik-e írás a rendszer eseménynaplójába. Ha igen, akkor megjelenít egy szöveget. Szinkron hívásnál ez úgy néz ki, hogy lefut a lekérdezés és ha ezalatt - ami ezredmásodpercben mérhető - nem történt írás a naplóba, akkor nem kapunk eredményt. Ugyanez aszinkron eljáráshívásnál úgy néz ki, hogy amikor írás történik, a rendszer értesíti a hívó rutint és átadja neki az eredményt. Miért nem lehet mindezt egy ciklus segítségével megoldani? Meg lehet, de egyrészt feleslegesen bonyolítja a forráskódot, másrészt értékes processzoridőt foglal, harmadrészt a rendszer teljes mértékben támogatja az aszinkron hívás típusokat. Az eredmény megérkezésére várva lehet folytatni a program futtatását, elindíthatók más eljárások egy másik szálon. Más megközelítésben a szinkron hívásoknál a program nem képes mást csinálni amíg a futás tart, míg az aszinkronnál igen. Rögtön "kézzelfoghatóvá" válik a különbség, ha eredményül nagy adatmennyiséget kapunk: szinkron hívásnál a feldolgozás végéig a program nem képes tovább lépni, erre az időre "kifagy". A rendszertámogatásról: a WMI aszinkron és szinkron elérését gyakorlatilag az összes programozási nyelvből meg lehet valósítani, kivéve a Webes ASP oldalakat. Példaként nézzük, hogy a Scripting API-n keresztül VBScript-ekből hogy lehet megvalósítani egy aszinkron elérést:
A Scripting API rengeteg metódust tartalmaz mindkét típushoz. Cikksorozatunkban legtöbbször az "ExecQuery"-t használtuk, amikor információra volt szükségünk az WMI-ból. Ez egy tipikus szinkron lekérdezés. Ugyanennek az aszinkron megfelelője az "ExecQueryAsync".
A WINMGMTS szolgáltatás használatával teremtsünk kapcsolatot a WMI-val.
Set service = GetObject("winmgmts:")
Létre kell hozni egy úgynevezett objektum "sink"-et.
Set sink = WScript.CreateObject("WbemScripting.SWbemSink","SINK_")
Készítsünk egy szubrutint a feldolgozandó eseményekhez. Amikor lefut egy aszinkron hívás, akkor keletkezik egy "OnCompleted" esemény - dolgozzuk fel. Az eljárástörzset példánkban egy szöveg megjelenítése alkotja. Ezek a sorok alkotják a szkript törzset.
Sub SINK_OnCompleted(iHResult, objErrorObject, objAsyncContext)
    WScript.Echo "Aszinkron művelet vége"
End Sub
A hívást adjuk át paraméterként a "Win32_Process" osztálynak, amely a rendszerben futó folyamatokat kezeli.
service.InstancesOfAsync sink, "Win32_Process"
Megakadályozandó, hogy a szkript befejezze működését, írassunk ki egy szöveget. Ez egy "OK" gombbal ellátott ablakban jelenik meg. Amíg nem kattintunk rá a gombra, a szkript futni fog.
WScript.Echo "Várakozás"
Ha rákattintunk az "OK"-ra, mert be akarjuk fejezni a futtatást, akkor töröljük az aszinkron hívást a "Cancel" metódussal.
objwbemsink.Cancel()
Majd töröljük a "Sink" objektumot is.
set objwbemsink= Nothing
Utóbbi két eljárásra nem feltétlenül van szükség, csak a teljesség kedvéért közöltük.
Nézzünk egy másik példát:
Kérdezzük le a rendszerben pillanatnyilag futó folyamatokat (processzeket) aszinkron hívással. A lekérdezés akkor fejeződik be, amikor az összes példánnyal végeztünk.
set oSink = WScript.CreateObject("wbemscripting.swbemsink","sink_")
Ismét a WINMGMTS objektumon keresztül kapcsolódunk a WMI-hoz. A folyamatokat a "Win32_Process" osztály kezeli, amely a "root\cimv2" névtérben található. Definiáljunk egy logikai változót, feladata annak jelzése, hogy a lefutott-e a hívás.
set oSvc = GetObject("winmgmts:root\cimv2")
bdone = false
Elegendő, ha csak a folyamat nevét kérdezzük le a többi tulajdonságra nincs szükségünk. Ciklikusan iktassunk be egy várakozást, egészen addig, amíg nem hajtódott végre a hívás.
osvc.ExecQueryAsync oSink, "SELECT Name FROM Win32_Process"'
    while not bdone    
   wscript.sleep 1000
wend
Készítsünk egy szubrutint, ahol kezeljük le az "OnObjectReady" eseményt. Ez akkor fut le, ha a lekérdezés eredménnyel járt és kaptunk egy folyamat nevet.
sub sink_OnObjectReady(oInst, octx)
   WScript.Echo "Futó folyamat: " & oInst.Name
end sub
Ha lefutott az aszinkron hívás (OnComplete) megjelenítünk egy feliratot és jelezzük a szkript törzsnek, hogy befejezheti a futást (bdone = true).
sub sink_OnCompleted(HResult, oErr, oCtx)
    WScript.Echo "Aszinkron hívás vége"
    bdone = true
end sub