C# - Távoli eljáráshívás aszinkron módon

forráskód letöltése
Ha olyan elosztott alkalmazásokat készítünk, melyek egymás függvényeit hívják a Remoting segítségével, akkor előfordulhat olyan helyzet, hogy egy lassabb hálózati kapcsolat, vagy egy bonyolultabb feladat elvégzése folyamán túl hosszú ideig tart, amíg a függvény hívás eredménye megérkezik. Ha szinkron módon meghívjuk a függvényt, akkor a hívó alkalmazás „lefagy”, amíg a válasz meg nem érkezik. Így ha a válasz nem érkezik meg igen gyorsan, akkor ez a módszer nem jó megoldás. Ekkor használható az aszinkron hívás, mely egy külön szálon futtatja a függvényhívást, így alkalmazásunk tovább folytathatja munkáját, amíg a válasz megérkezik.
A mellékelt példában a 2. évfolyam 8. számban megjelent „Remoting, avagy távoli függvényhívások megvalósítása” című cikkhez tartozó példát fogjuk most továbbfejleszteni úgy, hogy képes legyen aszinkron módon hívni a létrehozott függvényt. A mostani cikkben nem ismételjük azokat a tudnivalókat, melyet az említett cikkben már leírtunk, így az ottaniak ismerete elengedhetetlen e cikk megértéséhez.
A mellékelt példában csupán az RClient projekten kell változtatnunk, hiszen itt kell a távoli függvény hívást kezdeményeznünk. Mivel ennek módja változik most szinkronról aszinkronra, így csupán ezt a részt kell átírnunk a Form1.cs forráskódjában.
A sikeres kapcsolatfelvétel után a Button1 lenyomásakor történik a függvényhívás. Az aszinkron hívást úgy valósítjuk meg, hogy készítünk egy ún. callback függvényt, mely akkor kerül majd meghívásra, amikor a távoli függvényhívás visszatér az eredménnyel. Így alkalmazásunk rögtön értesülhet ezen eseményről. Ehhez azonban szükségünk lesz egy delegate-re, melyet úgy kell deklarálunk, hogy annak visszatérési értéke és paraméter listája megegyezik a távoli függvénnyel.
    public delegate string CallbackDelegate(int a, int b);
Készítsük most el a callback függvényünket. Itt paraméterként egy IAsyncResult interfészt kapunk. Ezt felhasználva elérhetjük az általunk létrehozott CallbackDelegate-et, melynek EndInvoke függvényét meghívjuk. Ez a hívás azt eredményezi, hogy visszatérési értékként megkapjuk a távoli függvény visszatérési értékét, melyet szabadon felhasználhatunk.
    public void CallbackFunction(IAsyncResult ar)
    {
      CallbackDelegate cd = (CallbackDelegate)((AsyncResult)ar).AsyncDelegate;      
      label2.Text=cd.EndInvoke(ar);
    }
Nézzük hogy is történik az aszinkron hívás: szükségünk lesz AsyncCallback nevű osztály példányára, melynek konstruktorában meg kell adnunk a callback függvényünket.
    private void button1_Click(object sender, System.EventArgs e)
    {
      AsyncCallback cb = new AsyncCallback(CallbackFunction);
Ezután létrehozatunk egy példányt a saját CallbackDelegate nevű delegate-ből. Ennek paraméterként a távoli objektumnak azt a függvényét kell megadnunk, melyet aszinkron módon szeretnénk meghívni.
      CallbackDelegate cd = new CallbackDelegate(rc.AddMethod);      
Végső lépésként a BeginInvoke függvénnyel elindítjuk a távoli függvény hívását. A BeginInvoke paraméter listája mindig egyezik a távoli függvény paraméter listájával, csak kiegészül két másik paraméterrel is. Így a paraméter lista első elemeiben azokat az értékeket kell megadnunk, melyeket a távoli függvénynek szeretnénk átadni, majd ezt követően megadhatjuk a már létrehozott callback függvényre való hivatkozást, hogy a távoli függvény futásának végén legyen mit meghívnia a külön szálon futó folyamatnak.
      IAsyncResult ar = cd.BeginInvoke(Convert.ToInt32(textBox1.Text), Convert.ToInt32(textBox2.Text), cb, null);
    }            
Annak szimulálására, hogy a meghívott függvény munkája időben sokáig tart, tegyünk egy üres ciklust a RObject projekt Class1.cs forráskódjában lévő AddMethod függvénybe. Hogy e ciklus meddig fusson, az az adott gép sebességétől függ, célszerű egy olyan értéket választani, hogy az AddMethod függvény futási ideje 3-4 másodpercig tartson.
    public String AddMethod(int a, int b) 
    {
      for (int i=0; i<1000000000; i++)
      {
      }
      return a.ToString() + " + " + b.ToString() + " = " + (a+b).ToString();
    }