A join minta egy konkurrens (párhuzamos, elosztott) programtervezési minta, aminek lényege, hogy a szálak üzenetátadással kommunikálnak. A zárolással szemben magas szintű programozási modell, ami kommunikációs szerkezetmodellt használ, hogy elvonatkoztasson a környezet bonyolultságától, és lehetővé tegye a skálázást. Az üzenetek elfogyasztását atominak tekinti, még akkor is, ha több csatornán érkeztek az üzenetek.

Ez a minta a join-kalkuluson alapul, és mintaillesztést használ. Konkrétan ezt azzal valósítja meg, hogy lehetővé teszi függvények join definícióját, és/vagy csatornákat, amelyek konkurens hívásra illeszkednek, továbbá üzenetminták is definiálhatók. Konkurens programtervezési minták mintája (metaminta), mivel egyszerűbbé és hatékonyabbá teszi ezeknek az entitásoknak a kommunikációt és a többszálú programozási paradigma használatát.

Leírása szerkesztés

A join-minta egy magasabb csővezeték, szinkronizációval és mintaillesztéssel. Angolul chordnak is nevezik. Valójában a fogalom összegezhető illesztéssel és csatlakozással néhány üzenetre, amelyek különböző üzenetsorokból származnak, majd egyetlen kezelővel egyszerre kezeli őket.[1] Az első elvárt kommunikációs lépés jelezhető a when kulcsszóval, a felcsatlakozás és párosítás and kulcsszóval, és a különféle feladatok futtatása a do kulcsszóval. A join minta tipikus formája:

j.When(a1).And(a2). ... .And(an).Do(d)

A When(a1) első argumentuma lehet szinkron vagy aszinkron csatorna, de lehet csatornák tömbje is. Minden további argumentumnak aszinkron csatornának kell lennie.[2]

Pontosabban, ha egy üzenet illeszkedik minták egy összekapcsolt sorozatára, akkor elindul a kezelője egy új szálban, ha aszinkron kontextusban van, különben az üzenetnek várnia kell addig, amíg nem illeszkedik egy minta az ő valamelyik mintájára. Ha több illeszkedés van, akkor nem specifikált, hogy melyik minta választódik ki.[3] Egy eseménykezelővel szemben, ami egy időben egyszerre több alternatív esemény egyikét szolgálja ki, a join minta csatornák konjunkciójára vár, és verseng a végrehajtásért minden más megengedett mintával.[4]

 
Folyamatdiagram a join minta végrehajtásáról; általános illesztés több üzenetre (wait a chord) és az erőforrások szinkronizálása (szabad vagy zárolva)

A join mintát pi-kalkulusbeli csatornák halmaza definiálja, ami két műveletet definiál, a küldést és a fogadást; emiatt két join kalkulusbeli névre van szükség, egy x csatornanévre és egy x függvénynévre, az érték megszerzéséhez (egy kérés). A join definíció jelentése x() egy hívása, ami visszaadja azt az értéket, ami egy x<> csatornán érkezett. Továbbá ha több függvény fut párhuzamosan, kiváltódik a return folyamat, és együtt szinkronizálódik a többi folyamattal.[5]

J ::= //join patterns
| x<y> //message send pattern
| x(y) //function call pattern
| J | JBIS //synchronization

A kliens perspektívájából a csatorna csak deklarál egy metódust, ugyanazzal a névvel és szignatúrával. A kliens üzenetet küld vagy kérést vált ki a metódus hívásával. A folytató metódusnak várnia kell, amíg kérés vagy üzenet érkezett minden csatornán a folytatás When mondatát követően. Ha a folytatás futni kezd, akkor a csatornahívások argumentumai kikerülnek a sorból (elfogyasztódnak), és atomian átadódnak a folytatás paramétereinek. [6]

 
A join minta osztálydiagramja

A legtöbb esetben a szinkron hívások sorrendje nem garantált a jobb teljesítmény érdekében. Az üzeneteket egy másik szál is elfogyaszthatja, így a szálnak lehet, hogy tovább kell várnia egy újabb üzenetre.[7]

Története szerkesztés

π-kalkulus – 1992 szerkesztés

A π-kalkulus a folyamatkalkulusok családjába tartozik, aminek segítségével matematikai képletekkel írja le és elemzi a konkurens számítások sorozatát elnevezett csatornák bevezetésével, így a folyamatok kommunikálhatnak csatornákon keresztül, így leírhatók olyan konkurens számítások is, amelyek hálózatkonfigurációja dinamikusan, futásidőben változik.

Join kalkulus – 1993 szerkesztés

A join minták először Fournet és Gonthier alapvető join kalkulusával jelentek meg, mint aszinkron folyamatalgebra, amit elosztott környezetben hatékony implementációhoz terveztek.[8] A join kalkulus kifejezőképessége megegyezik a π-kalkuluséval. Arra fejlesztették ki, hogy formális megalapozást adjon az elosztott programozási nyelvekhez, így elkerüli a más folyamatkalkulusokban használt kommunikációs szerkezeteket, például a randevúkat.

Elosztott join kalkulus – 1996 szerkesztés

A join kalkulus névátadó kalkulus, és egyben magnyelv a konkurens és elosztott programozáshoz.[9] Az elosztott join kalkulus a join kalkuluson alapul, de az elosztott számításokhoz kibővítve.[10] Mobil ágenseket használ, ahol az ágensek nem egyszerűen programok, hanem futó folyamatok, a saját kommunikációs képességeikkel.

JoCaml, Funnel és Join Java – 2000 szerkesztés

A JoCaml[11][12] és a Funnel[13][14] funkcionális programozási nyelvek, amelyek támogatják a join minták deklarálását. Közvetlenül valósítják meg a join kalkulust funkcionális programozási nyelvekben.

Egy másik kiterjesztés egy nem szabványos kiterjesztés, a Java, JoinJava, amit Itzstein és Kearney javasolt.[15]

Polyphonic C# – 2002 szerkesztés

Cardelli, Benton és Fournet javasolta a C# bővítését join mintával 2002-ben, ez a Polyphonic C#.

Cω – 2003 szerkesztés

A Cω a join kalkulus objektumorientált adaptációja. Tartalmazta a Polyphonic C#-ot a Cω (Comega) 2004-es kiadásában.[16]

Scala Joins – 2007 szerkesztés

A Scala Joins egy programkönyvtár, ami lehetővé teszi a join minta alkalmazását a Scala nyelvben a kiterjeszthető mintaillesztés kontextusában, hogy a join mintát a létező aktor alapú keretrendszerbe integrálja.

JErlang – 2009 szerkesztés

Az Erlang natívan támogatja a konkurens, valós idejű és elosztott paradigmát. A folyamatok közötti konkurenciát bonyolult volt megoldani, ezért egy új nyelvet alkottak, a JErlangot, ami a join kalkuluson alapul.

A klasszikus programozási irodalomban szerkesztés

Join mintával egyszerűen kódolhatók egymással kapcsolatban álló konkurens problémák, mint aktorok és aktív objektumok.[17]

class SymmetricBarrier {
public readonly Synchronous.Channel Arrive;
public SymmetricBarrier(int n) {
    // create j and init channels (elided)
    var pat = j.When(Arrive);
    for (int i = 1; i < n; i++) pat = pat.And(Arrive);
    pat.Do(() => { });
}
}
var j = Join.Create();
Synchronous.Channel[] hungry;
Asynchronous.Channel[] chopstick;
j.Init(out hungry, n); j.Init(out chopstick, n);
for (int i = 0; i < n; i++) {
    var left = chopstick[i];
    var right = chopstick[(i+1) % n];
    j.When(hungry[i]).And(left).And(right).Do(() => {
    eat(); left(); right(); // replace chopsticks
    });
}
class Lock {
public readonly Synchronous.Channel Acquire;
public readonly Asynchronous.Channel Release;
    public Lock() {
        // create j and init channels (elided)
        j.When(Acquire).And(Release).Do(() => { });
        Release(); // initially free
    }
}
class Buffer<T> {
public readonly Asynchronous.Channel<T> Put;
public readonly Synchronous<T>.Channel Get;
    public Buffer() {
        Join j = Join.Create(); // allocate a Join object
        j.Init(out Put);
        // bind its channels
        j.Init(out Get);
        j.When(Get).And(Put).Do // register chord
        (t => { return t; });
    }
}
class ReaderWriterLock {
private readonly Asynchronous.Channel idle;
private readonly Asynchronous.Channel<int> shared;
public readonly Synchronous.Channel AcqR, AcqW,
RelR, RelW;
public ReaderWriterLock() {
    // create j and init channels (elided)
    j.When(AcqR).And(idle).Do(() => shared(1));
    j.When(AcqR).And(shared).Do(n => shared(n+1));
    j.When(RelR).And(shared).Do(n => {
    if (n == 1) idle(); else shared(n-1);
    });
    j.When(AcqW).And(idle).Do(() => { });
    j.When(RelW).Do(() => idle());
    idle(); // initially free
}
}
class Semaphore {
public readonly Synchronous.Channel Acquire;
public readonly Asynchronous.Channel Release;
    public Semaphore(int n) {
        // create j and init channels (elided)
        j.When(Acquire).And(Release).Do(() => { });
        for (; n > 0; n--) Release(); // initially n free
    }
}

Alapfogalmak szerkesztés

  • Join kalkulus: A join minta első megvalósításai ezen alapulnak.
  • Üzenetátadás: A join minta párhuzamossági okok miatt üzenetátadással működik.
  • Csatorna: Csatornák szinkronizálják és adnak át üzeneteket a konkurensen végrehajtott szálak között. Általában egy csatorna több join mintában is benne van, minden minta egy lehetséges folytatást definiál, ami lefuthat, ha a csatorna meghívódik.[6]
  • Szinkron: A join minta használhat szinkron csatornákat, amelyek eredményt adnak vissza. A szinkron minta folytatása a szinkron hívó szálában fut.[6]
  • Aszinkron: Az aszinkron csatornának nincs visszatérési értéke, de argumentumai lehetnek. Folytatása egy új szálban fut. A join minta teljesen aszinkron lehet, folytatását szubrutinban nyújtja, és When mondata csak aszinkron csatornákat tartalmaz.[6]
  • Szinkron és aszinkron: A szinkron és aszinkron bufferek kombinációja egy olyan modult eredményez, ami kétféle fogyasztót támogat.[6]
  • Ütemező: a join mintákat ütemezni kell, kell egy ütemező, ami ezt megoldja.[6]
  • Programtervezési minták: a join minta konkurens viselkedési programtervezési minta.
  • Konkurens programozás: Konkurensen hajtódik végre.
  • Mintaillesztés: a join minta illeszkedő feladatokkal működik.
  • Párhuzamos programozás: párhuzamosan hajtódnak végre a feladatok.
  • Elosztott programozás: a join mintával felosztott feladatokat különböző ágensek hajthatják végre különböző környezetekben.
  • Tranzakcionális memória: alkalmas a join minta kommunikációjának megvalósítására.
  • Átfedés: a join minta számára nem probléma a különböző join minták közötti átfedés, egy csatornára több join minta is hivatkozhat.

Alkalmazások szerkesztés

Mobil ágenesek szerkesztés

A mobil ágensek autonóm programágensek társas készségekkel, kommunikációs lehetőségekkel és mozgási lehetőségekkel. A program és adatai mozoghatnak különféle gépek között futás közben.

A mobil ágensek használhatók arra, hogy összekapcsolják a konkurens és az elosztott programozást join kalkulussal. Ezért megalkották az elosztott join kalkulus fogalmát, ami helyekkel és primitívekkel bővíti a join kalkulust a mobilitás leírására. Így az ágenseknek van fogalmuk a lokációról, ami az ágens fizikai helyét jelenti. Az ágens atomi jelleggel mozoghat a különböző helyek között.[22]

Egy ágens folyamata funkcionalitásainak halmazaként definiálható, amibe beletartozik az aszinkron üzenetátadás, és a vándorlás. A mozgás könnyebb reprezentációja érdekében a helyeket fába szervezik. Ezzel a hibázás is egyszerűbben modellezhető. Ha egy helyen összeomlik a program, akkor minden alatta levő helyen is összeomlik. Egy meghatározott helyű hiba bármely futási helyről megtalálható, ez segít kijavítani a hibát.[22]

Így a join kalkulus egy elosztott nyelv magja. Operációs szemantikája egyszerűen implementálható egy elosztott rendszerben, ami a hiba lehetőségét is tartalmazza. Így a join kalkulus elsőrangú objektumokként kezeli a csatornákat és a helyeket. Egy hely ismeri más helyek nevét, ezért onnan oda lehet lépni. Ez biztos alapot teremt a statikus elemzésnek és a biztonságos mozgásnak. Teljes az elosztott helyek kifejezésében. Ha nincs hiba, akkor a végrehajtás független a helyektől. Ez az átláthatóság fontos a mobil ágensek tervezésében, és nagyon hasznos tulajdonságaik ellenőrzéséhez.[22]

2007-ben kiadták az alap join kalkulus egy proaktív ágenseket kezelő kiegészítését. Az ágensek megfigyelhetik közös környezetüket. Ehhez a közös környezethez változók adhatók az ágensekkel, például egy névszolgáltató, ami segít megtalálni más ágenseket.[23]

Szerkesztés, fordítás szerkesztés

A join nyelvek join kalkulusra, mint magnyelvre épülnek. Ezért a kalkulust aszinkron folyamatokkal elemzik, és a join minták modellt adnak az eredmény szinkronizálására.[9]
Ehhez két fordító szükséges:

  • Join fordító: a join nyelv számára készített fordító. Ezt csak a join kalkulus számára hozták létre.
  • Jocaml fordító: Az Objectif Caml egy kiterjesztésének fordítója, ami a join kalkulust használja.[24]

Ez a két fordító ugyanazon a rendszeren, egy automatán működik.

let A(n) | B() = P(n)
and A(n) | C() = Q(n)
;;

Ez egy üzenet fogyasztását reprezentálja, ami a kiegészített join modellbe érkezik. Minden állapot egy lehetőség a kód végrehajtására, és minden átmenet üzenet fogadása két állapot között. Ha minden üzenet meg van ragadva, akkor a fordító végrehajtja a join kód törzsét, a teljes join modellnek megfelelően.

Így a join kalkulusban az alapértékek nevek, mint például A, B vagy C. Így a két fordító kétféleképpen reprezentálja az eredményeket. A join fordító egy kétdimenziós vektort használ, az első a név, a második pedig a fogadott üzenetek listája. A Jocaml a neveket pointerként használja, ami a definícióra mutat. Ezek további nevek pointerét tartalmazzák állapot mezővel, és illeszkedő adatszerkezetet üzenetekkel. Az alapvető különbség az, hogy ha az őrző folyamat végrehajtódik, akkor először az üzenetben szereplő összes nevet ellenőrzi, hogy készen állnak-e a futásra, míg a második csak egy változót és hozzáférést a többihez ahhoz, hogy tudja, a modell teljes.[9]

Jelenlegi kutatások szerint a fordítási séma kétlépéses: irányításból és továbbításból áll. Az irányító tervezése és korrektsége lénygében a mintaillesztés elméletén múlik, míg egy belső irányítási lépés beszúrása a kommunikációba természetes ötlet, ami intuitívan nem változtatja meg a folyamat működését. Megfigyelték, hogy nem érdemes ennek ellenőrzése érdekében felügyelő objektumokat futás idejű szinten beiktatni, mivel ez jelentősen bonyolultabbá teszi az üzenetsorok kezelését, mivel végig kellene őket szkennelni a mintaillesztés és az üzenet elfogyasztása előtt.[25]

Implementációk és könyvtárak szerkesztés

A join mintákat több nyelvben is megtalálhatjuk. Egyes nyelvek implementációja is a join mintákon alapul, ilyen például a Polyphonic C# és az MC#.[26] Más nyelvek könyvtárként tartalmazzák a join mintákat. Ilyen a Scala Joins a Scala számára,[27] vagy a VB Joins könyvtára.[28] Továbbá egyes nyelvekben a join mintával fejlesztik a join mintákat, ilyen például a Scheme.[29]

JErlang CB Joins Library Polyphonic C# Parallel C# Scala Joins F# Scheme Join Java Hume JoCaml
Mintaillesztés Igen Igen Igen Igen Igen Igen Igen Igen Igen Igen Igen Igen
Ütemező a join minták között Igen : első illesztés Igen : első/round robin Igen Igen Igen Igen Igen Igen Nem Igen : véletlen Igen : első/round robin Igen : véletlen
Generikusok Igen n. a. Igen Nem n. a. Nem Igen Igen Nem Nem Nem Nem
Felüldefiniálás Nem Igen n. a. n. a. n. a. Igen Igen Igen Igen Igen Nem Nem

Join Java szerkesztés

A Join Java egy programozási nyelv, ami a Javán alapul, és amiben használhatók join minták.[30] Ezt három új nyelvi szerkezet bevezetésével éri el:

A join metódusokat két vagy több join töredékkel lehet definiálni. A join metódus ezeket próbálja meg végrehajtani. Ha a visszatérési típus szabvány Java típus, akkor a vezető töredék blokkolja a hívót, amíg a join minta nem végez, és a metódus is be nem fejeződik. Ha a visszatérési érték jel, akkor a vezető töredék azonnal visszatér. A több töredék aszinkron, nem blokkolja a hívót.

Példa:

 class JoinExample {
    int fragment1() & fragment2(int x) {
       //will return value of x
       //to caller of fragment1
       return x;
    }
 }

Az aszinkron metódusokat jel visszatérési típusúak. Ugyanazok a jellemzői, mint a void visszatérési típus, kivéve hogy azonnal visszaér, és törzsét egy új szál hajtja végre.

Példa:

 class ThreadExample {
    signal thread(SomeObject x) {
       //this code will execute in a new thread
    }
 }

Rendezési módosítók: a join töredékek különböző join mintákban szerepelnek, így lehet, hogy egy minta hívásával több join minta is illeszkedik. Az alábbi példában, ha B(), C() és D() meghívódik, akkor A() is meghívódik. Az A() töredék a minták közül háromra illeszkedik, így három metódus is meghívható. A rendezési osztálymódosító meghatározza, hogy ekkor melyik hívódjon meg; alapértelmezetten véletlenül választódik ki. A rendezési módosító prioritást állíthat fel, hogy melyik legyen előnyben.

Példa:

 class ordered SimpleJoinPattern {
    void A() & B() {
    }
    void A() & C() {
    }
    void A() & D() {
    }
    signal D() & E() {
    }
 }

A legközelebbi rokon nyelv a Polyphonic C#.

JErlang szerkesztés

Erlangban nem egyszerű megoldani a párhuzamos folyamatok szinkronizációját. Ezért hozták létre a JErlang nyelvet, ami az Erlang kiterjesztése.[31] A J betű a Join rövidítése. A bővítés elemei:

A joinok lehetővé teszik az első illesztés szemantikáját; lehetőséget ad több minta definiálására, és az üzenetek sorrendjének megőrzésére.

operation() ->
    receive
        {ok, sum} and {val, X} and {val, Y} ->
            {sum, X + Y};
        {ok, mult} and {val, X} and {val, Y} ->
            {mult, X * Y};
        {ok, sub} and {val, X} and {val, Y} ->
            {sub, X - Y};
    end
end

Az őrök további szűrést tesznek lehetővé minták nélkül. Korlátozott számú kifejezés mellékhatás nélkül.

receive
    {Transaction, M} and {limit, Lower, Upper}
        when (Lower <= M and M <= Upper ) ->
    commit_transaction(M, Transaction)
end

Nemlineáris mintákkal az üzenet több joinra illeszthető.

receive
    {get, X} and {set, X} ->
        {found, 2, X}
end
...
receive
    {Pin, id} and {auth, Pin} and {commit, Id} ->
        perform_transaction(Pin, Id)
end

A terjesztés lehetővé teszi, hogy az üzenetek törlés helyett másolódjanak.

receive
    prop({session, Id}) and {act, Action, Id} ->
        perform_action(Action, Id);
    {session, Id} and {logout, Id} ->
        logout_user(Id)
end
...
receive
    {Pin, id} and {auth, Pin} and {commit, Id} ->
        perform_transaction(Pin, Id)
end

Szinkron hívások

receive
    {accept, Pid1} and {asynchronous, Value}
                   and {accept, Pid2} ->
        Pid1 ! {ok, Value},
        Pid2 ! {ok, Value}
end

C++ szerkesztés

Yigong Liu írt néhány osztályt a C++-hoz a join minta számára,[32] köztük hasznos eszközöket, mint a szinkron és aszinkron csatornák, chordok, és hasonlók. Beépült a Boost projektbe.[33]

template <typename V>
class buffer: public joint {
public:
  async<V> put;
  synch<V,void> get;
  buffer() {
    chord(get, put, &buffer::chord_body);
  }
  V chord_body(void_t g, V p) {
    return p;
  }
};

Ebben a példában egy szálbiztos buffer és egy üzenetsor látható a put és get alapműveletekkel.[34]

C# szerkesztés

Polyphonic C# szerkesztés

A Polyphonic C# a C# programozási nyelv kiterjesztése. Új konkurencia modellt vezet be szinkron és aszinkron metódusokkal és chordokkal.

public class Buffer {
   public String get() & public async put(String s) {
      return s;
   }
}

A példa egy egyszerű buffert mutat.[35]

MC# szerkesztés

Az MC#[36] nyelv a Polyphonic C# adaptációja konkurens elosztott programozáshoz.

public handler Get2 long () & channel c1 ( long x )
& channel c2 ( long y )
{
return ( x + y );
}

A példa a chordot, mint szinkronizációs eszközt mutatja.

Parallel C# szerkesztés

A Parallel C#[37] a Polyphonic C# bővítése új fogalmakkal, mint mozgatható metódusok és magasabb rendű függvények.

using System;
 
class Test13 {
 int Receive() & async Send( int x ) {
  return x * x;
 }
 
 public static void Main( string[] args ) {
  Test13 t = new Test13();
  t.Send( 2 );
  Console.WriteLine( t.Receive() );
 }
}

A példa a joinok használatát mutatja.[38]

szerkesztés

A új nyelvi eszközöket ad a konkurrens programozáshoz a Polyphonic C# egy korábbi verziójához képest. A Joins Concurrency Library C# könyvtár és a többi .NET nyelv ebből a projektből indult ki.[39][40]

Scalable Join Patterns szerkesztés

A Scalable Join Patterns egy programkönyvtár, amit arra terveztek, hogy könnyen használható legyen. A Russo könyvtárral szemben,[28] nem tartalmaz globális zárat. Az atomi üzenetrendszeren és az összehasonlít-cserél rendszeren alapul. A könyvtár [41] a join minta következő kiegészítéseit tartalmazza:

  • Nem használt erőforrásoknak érkezett üzenetek ellopása
  • Lusta sormentés, ami allokációkor és potenciálisan a processzorközi kommunikációban az allokáció vagy a sorba tétel elkerülésével egy optimista gyors-úttal;
  • Egy "WOKEN" állapot, ami biztosítja, hogy egy blokkolt szinkron hívó csak egyszer hívódik meg.

JoCaml szerkesztés

A JoCaml az első nyelv, amiben a join mintát implemtálták. Eleinte a többi implementációt is a JoCaml fordítóval fordították.[42] A JoCaml nyelv az OCaml kiterjesztése. A kiterjesztés támogatja a konkurenciát és a szinkronizációt, a programok elosztott végrehajtását, és a dinamikus költözést az aktívan futó programtöredékek számára[43]

type coins = Nickel | Dime
and drinks = Coffee | Tea
and buttons = BCoffee | BTea | BCancel;;

(* def defines a Join-pattern set clause
 * "&" in the left side of = means join (channel synchronism)
 * "&" in the right hand side means: parallel process
 * synchronous_reply :== "reply" [x] "to" channel_name
 * synchronous channels have function-like types (`a -> `b)
 * asynchronous channels have types (`a Join.chan)
 * only the last statement in a pattern rhs expression can be an asynchronous message
 * 0 in an asynchronous message position means STOP ("no sent message" in CSP terminology).
   *)

def put(s) = print_endline s ; 0 (* STOP *)
  ;; (* put: string Join.chan *)

def serve(drink) = match drink with
                 Coffee -> put("Cofee")
                 | Tea -> put("Tea")
              ;; (* serve: drinks Join.chan *)

def refund(v) = let s = Printf.sprintf "Refund %d" v in put(s)
    ;; (* refund: int Join.chan *)

let new_vending serve refund =
  let vend (cost:int) (credit:int) = if credit >= cost
                      then (true, credit - cost)
                      else (false, credit)
  in
  def coin(Nickel) & value(v) = value(v+5) & reply () to coin
  or coin(Dime) & value(v) = value(v+10) & reply () to coin

  or button(BCoffee) & value(v) =
     let should_serve, remainder = vend 10 v in
     (if should_serve then serve(Coffee) else 0 (* STOP *))
             & value(remainder) & reply () to button

  or button(BTea) & value(v) =
     let should_serve, remainder = vend 5 v in
     (if should_serve then serve(Tea) else 0 (* STOP *))
             & value(remainder) & reply () to button

  or button(BCancel) & value(v) = refund( v) & value(0) & reply () to button
  in spawn value(0) ;
  coin, button (* coin, button: int -> unit *)
  ;; (* new_vending: drink Join.chan -> int Join.chan -> (int->unit)*(int->unit) *)

let ccoin, cbutton = new_vending serve refund in
  ccoin(Nickel); ccoin(Nickel); ccoin(Dime);
  Unix.sleep(1); cbutton(BCoffee);
  Unix.sleep(1); cbutton(BTea);
  Unix.sleep(1); cbutton(BCancel);
  Unix.sleep(1) (* let the last message show up *)
  ;;

gives

Coffee
Tea
Refund 5

Hume szerkesztés

A Hume[44] egy szigorú, erősen típusozott funkcionális nyelv korlátozott erőforrásokkal rendelkező platformok számára, aminek konkurenciája aszinkron üzenetátadáson alapul, tartalmaz adatfolyam programozást. Szintaxisa hasonlít a Haskellhez.

A Hume nem tartalmaz szinkron üzenetátadást.

A join minta készletek közös csatornákat tartalmaznak, mint box (doboz), amely egy in tuple összes csatornáján hallgat, és minden lehetséges kimenetet egy out tupléban definiál.

Egy join mintának meg kell felelnie a box input tuple típusnak, amit '*' jelöl a nem igényelt csatornákra, kifejezéstípusa pedig megfelel az output tuple típusnak, amit '*' jelöl a nem táplált kimenetekre.

Egy wire mondat meghatározza a következőket:

  1. tuple a megfelelő input eredetekhez vagy forrásokhoz és opcionálisan kezdőértékek
  2. tuple az output célokhoz, amelyek csatornák vagy sinkek (stdout, ..).

Egy box specifikálhatnak kivételkezelőket az output tuplénak megfelelő kifejezésekkel.

data Coins = Nickel | Dime;
data Drinks = Coffee | Tea;
data Buttons = BCoffee | BTea | BCancel;

type Int = int 32 ;
type String = string ;
show u = u as string ;

box coffee
in ( coin :: Coins, button :: Buttons, value :: Int ) -- input channels
out ( drink_outp :: String, value :: Int, refund_outp :: String) -- named outputs

match
-- * wildcards for unfilled outputs, and unconsumed inputs
  ( Nickel, *, v)  -> ( *, v + 5, *)
| ( Dime, *, v)    -> ( *, v + 10, *)
| ( *, BCoffee, v) -> vend Coffee 10 v
| ( *, BTea, v)    -> vend Tea 5 v
| ( *, BCancel, v) -> let refund u = "Refund " ++ show u ++ "\n"
                      in ( *, 0, refund v)
;

vend drink cost credit = if credit >= cost
                      then ( serve drink, credit - cost, *)
                      else ( *, credit, *);

serve drink = case drink of
               Coffee -> "Cofee\n"
               Tea -> "Tea\n"
;

box control
in (c :: char)
out (coin :: Coins, button:: Buttons)
match
 'n' -> (Nickel, *)
 | 'd' -> (Dime, *)
 | 'c' -> (*, BCoffee)
 | 't' -> (*, BTea)
 | 'x' -> (*, BCancel)
 | _ -> (*, *)
;

stream console_outp to "std_out" ;
stream console_inp from "std_in" ;

-- dataflow wiring

wire cofee
    -- inputs (channel origins)
    (control.coin, control.button, coffee.value initially 0)
    -- outputs destinations
    (console_outp, coffee.value, console_outp)
;

wire control
    (console_inp)
    (coffee.coin, coffee.button)
;

Visual Basic szerkesztés

Concurrent Basic – CB szerkesztés

A Concurrent Basic a Visual Basic 9.0 kiterjesztése aszinkron konkurenciaszerkezetekkel a join minták kifejezésére. A CB a korábbi Polyphonic C#, Cω és Joins Library tapasztalatain alapulva adoptál egy egyszerű eseményszerű szintaxist, ami ismerős a VB programozók számára, és lehetővé teszi generikus konkurencia absztrakciók definiálását, továbbá természetesebb támogatást nyújt az öröklődésnek, megengedve az alosztálynak a minták bővítését. Egy CB osztály deklarálhat metódust, ami meghívódik ha kommunikációs esemény történt csatornák egy join mintát meghatározó halmazán.[28]

Module Buffer

  Public Asynchronous Put(ByVal s As String)
  Public Synchronous Take() As String

  Private Function CaseTakeAndPut(ByVal s As String) As String _
    When Take, Put
       Return s
  End Function

End Module

A példa bemutatja a Concurrent Basicbe bevezetett új kulcsszavakat: Asynchronous, Synchronous és When.[45]

Joins könyvtár (C# és VB) szerkesztés

Ez a könyvtár a join minta magas szintű absztrakciója, ami objektumokat és generikusokat használ. A csatornák speciális delegált értékek valamely közös Join objektum (metódusok helyett).[46]

 class Buffer {
   public readonly Asynchronous.Channel<string> Put;
   public readonly Synchronous<string>.Channel Get;
   public Buffer() {
     Join join = Join.Create();
     join.Initialize(out Put);
     join.Initialize(out Get);
     join.When(Get).And(Put).Do(delegate(string s) {
       return s;
     });
   }
 }

A példa azt mutatja, hogyan lehet egy Join objektum metódusait használni.[47]

Scala szerkesztés

A Scala Joins[48] egy Scala könyvtár a join minta használatához. A mintaillesztés egy eszköz join modellek készítéséhez. Példák itt: Join definitions in Scala

A nyelv mintaillesztési lehetőségei általánosíthatók, hogy lehetővé tegye a mintaillesztésben használt objektumok független reprezentációját. Most a könyvtárakban használható egy új típusú absztrakció. A join mintának az az előnye, hogy megengedi a különböző szálak szinkronizációjának deklaratív specifikációját. A join minta megfeleltethető egy véges állapotgépnek, ami specifikálja az objektum érvényes állapotait.

class ReaderWriterLock extends Joins {

private val Sharing = new AsyncEvent[Int]
val Exclusive, ReleaseExclusive = new NullarySyncEvent
val Shared, ReleaseShared = new NullarySyncEvent
join {
case Exclusive() & Sharing(0) => Exclusive reply
case ReleaseExclusive() => { Sharing(0); ReleaseExclusive reply }
case Shared() & Sharing(n) => { Sharing(n+1); Shared reply }
case ReleaseShared() & Sharing(1) => { Sharing(0); ReleaseShared reply }
case ReleaseShared() & Sharing(n) => { Sharing(n-1); ReleaseShared reply }
}
Sharing(0) }

Egy osztályban eseményeket definiálunk adattagként. Így lehet a join szerkezetet, hogy lehetővé tegye a mintaillesztést eset deklarációkkal. A lista => jellel kapcsolja össze a deklaráció részeit. A bal oldal join minta egy modellje, ami események szinkron vagy aszinkron kombinációját jelzi, a jobb oldal pedig végrehajtódik, ha a join minta teljesül.

Scalaban lehet az actor könyvtárat is használni.[49] a join mintával.Például egy nem korlátos buffer:[27]

val Put = new Join1[Int]
val Get = new Join
class Buffer extends JoinActor {
def act() {
receive { case Get() & Put(x) => Get reply x }
} }

A könyvtár támogatja az aktor alapú konkurenciát, és a join minta ennek kiterjesztése, így lehetséges kombinálni a join mintákat és az eseményvezérelt konkurenciamodellt, amit az aktorok nyújtanak. Ahogy a példában látható, ez ugyanaz, mint aktorokkal használni a join mintákat, csak eseteket kell deklarálni a fogadó metódusban, hogy megtudjuk, hogy a modell teljes.

Ugyanezek az eszközök elérhetők az F# számára is.

A Scala Join[50] és a Chymyst[51] a join minta újabb megvalósításai, amelyek Dr. Philipp Haller Scala Joinsán[52] alapulnak.

Haskell szerkesztés

A Join Language[53] a join minta implementációja a Haskell számára.

Scheme szerkesztés

A join minta lehetővé tesz egy új programozási stílust speciálisan a többmagos architektúrák számára, ami sok programozási helyzetben elérhető, és magas szintű absztrakciót nyújt. Ez a Guardokon (őrök) és Propagationön (továbbításon) alapul. A következőkben ennek egy megvalósítását ismertetjük a Scheme nyelvben.[29]

Az őrök ahhoz fontosak, hogy csak az illeszkedő kulcsú adatok frissüljenek vagy lehessenek találatok. PA továbbítás töröl egy itemet, elolvassa a tartalmát és visszahelyez egy itemet a készletbe. Olvasás alatt az item megtalálható a készletben. Az őrök osztott változókkal fejezhetők ki. Az újdonság az, hogy a join minta tartalmazhatja a most továbbított és egyesített részeket. Így a Scheme-ben a továbbítás előtti vagy alatti részeket és az eltávolítás előtti és utáni részeket. A Goal-Based (cél alapú) használata ia munkát sok feladatra osztja és joinolja az összes végeredményt az összes join mintával. Egy "MiniJoin" nevű rendszert arra alkottak, hogy a köztes eredményt felhasználja más feladatok megoldásához, ha ez lehetséges. Ha ez nem lehetséges, akkor megvárja a többi feladatot, hogy felhasználhassa a végeredményüket.

Így a konkurens join minta alkalmazása párhuzamosan hajtódik végre egy többmagos rendszeren, ami viszont nem garantálja, hogy nem lesznek ütközések. Ezt a szoftvertranzakciós memória (STM) intézi egy erősen hangolt konkurens adatszerkezetben, ami atomi összehasonlításon és cserén (CAS) alapul. Ez megengedi, hogy sok konkurens művelet fusson párhuzamosan egy többmagos architektúrán. Továbbá atomi végrehajtást használ a CAS és STM közötti hamis konfliktus elkerülésére.[29]

Hasonló minták szerkesztés

A join minta nem az egyetlen, ami alkalmas egyszerre több szál futtatására, de az egyetlen, ami lehetővé teszi a kommunikációt az erőforrások, a szinkronizáció és a különböző folyamatok joinja között.

  • Szekvencia minta: az egyik feladatnak meg kell várnia, amíg az előző végez (klasszikus implementáció).
  • Split minta: párhuzamosan végrehajt néhány feladatot.

Jegyzetek szerkesztés

  1. Taral Dragon: Join Calculus, 2009. október 25. (Hozzáférés: 2012)
  2. (2008. október 23.) „Join Patterns for Visual Basic”, Nashville, Tennessee, USA, 10. o.  
  3. Parallel C#. (Hozzáférés: 2012)
  4. „Join Patterns for Visual Basic”, 2. o.  
  5. (2002) „The Join Calculus: a Language for Distributed Mobile Programming”, Caminha, 8. o.  [halott link]
  6. a b c d e f Join Patterns for Visual Basic Claudio V. Russo.
  7. (2008. október 23.) „Join Patterns for Visual Basic”, Nashville, Tennessee, USA, 5. o.  
  8. (2008. október 23.) „Join Patterns for Visual Basic”, Nashville, Tennessee, USA, 18. o.  
  9. a b c (2007. szeptember 25.) „Compiling Join-Patterns”, Le Chesnay France.  
  10. (1996) „A Calculus of Mobile Agents”, Le Chesnay, 406–421. o, Kiadó: Concurrency Theory.  [halott link]
  11. (2000. szeptember 1.) „JoCaml: a language for concurrent distributed and mobile programming.”. In Advanced Functional Programming, 4th International SchoolOxford, August 2002 2638.  
  12. (1999) „JoCaml: Mobile agents for Objective-Caml”. In First International Symposium on AgentSystems and Applications. (ASA'99)/Third International Symposium onMobile Agents (MA'99).  
  13. (2000. szeptember 1.) „An overview of functional nets.”. Summer School, Caminha, Portugal, September 2000 2395.  
  14. (2000) „Functional nets.”. In Proceedings of the European Symposium on Programming. Lecture Notes in Computer Science 1782.  
  15. (2001) „Join Java: An alternative concurrency semantics for Java”. Echnical Report ACRC-01-001, University of South Australia.  
  16. (2002. június 1.) „Modern concurrency abstractions for C#.”. In Proceedings of the 16th European Conference on Object-Oriented Programming (ECOOP 2002), number 2374 in LNCS.  
  17. Singh, Satnam (2007. január 6.). „Higher Order Combinators for Join Patterns using STM”, 1. o.  
  18. a b (2011. október 27.) „Scalable Join Patterns”, Portland, Oregon, USA, 4. o.  
  19. (2011. október 27.) „Scalable Join Patterns”, Portland, Oregon, USA, 1. o.  
  20. a b (2011. október 27.) „Scalable Join Patterns”, Portland, Oregon, USA, 3. o.  
  21. (2011. október 27.) „Scalable Join Patterns”, Portland, Oregon, USA, 2. o.  
  22. a b c (1996) „A Calculus of Mobile Agents”, Le Chesnay, Kiadó: Concurrency Theory.  [halott link]
  23. (2007) „Multi-Agent Systems and Applications V”. Lecture Notes in Computer Science 4696, 298–300. o. DOI:10.1007/978-3-540-75254-7_30.  
  24. http://pauillac.inria.fr/jocaml/ Archiválva 2005. október 1-i dátummal a Wayback Machine-ben Jocaml Compiler
  25. (2004. április 5.) „Compiling Pattern Matching in Join-Patterns”, 417–431. o, Kiadó: INRIA.  [halott link]
  26. http://www.mcsharp.net/
  27. a b (2008) „Implementing Joins using Extensible Pattern Matching”, Lausanne, 1–15. o, Kiadó: Coordination Models and Languages.  [halott link]
  28. a b c (2008. október 23.) „Join Patterns for Visual Basic”, Nashville, Tennessee, USA, 53–72. o.  
  29. a b c Parallel Join Patterns with Guards and Propagation”, Denmark.  
  30. (2002) „Hardware Join Java: A High Level Language For Reconfigurable Hardware Development”, Hong Kong. [2013. február 19-i dátummal az eredetiből archiválva]. (Hozzáférés: 2017. július 6.)  
  31. (2009) „JErlang: Erlang with Joins”, London. [2017. október 10-i dátummal az eredetiből archiválva]. (Hozzáférés: 2017. július 7.)  
  32. osztályok a join mintákhoz
  33. Boost c++.
  34. Join - Asynchronous Message Coordination and Concurrency Library, Hiba: Érvénytelen idő. (Hozzáférés: 2012)
  35. Introduction to Polyphonic C#. (Hozzáférés: 2012)
  36. MC#
  37. Parallel C#
  38. Parallel C#. [2013. november 26-i dátummal az eredetiből archiválva]. (Hozzáférés: 2012)
  39. The Joins Concurrency Library. (Hozzáférés: 2012)
  40. Comega. (Hozzáférés: 2012)
  41. (2011. október 27.) „Scalable Join Patterns”, Portland, Oregon, USA.  
  42. JoCaml nyelv
  43. JoCaml: a Language for Concurrent Distributed and Mobile Programming, Advanced Functional Programming, Lecture Notes in Computer Science. Springer-Verlag, 129-158. o. (2003) 
  44. Hammond/Michaelson/Sun - Programming reactive systems in Hume
  45. Concurrent Basic. [2015. április 25-i dátummal az eredetiből archiválva]. (Hozzáférés: 2012)
  46. (2007) „The Joins Concurrency Library”, Cambridge, 260–274. o, Kiadó: Practical Aspects of Declarative Languages.  [halott link]
  47. The Joins Concurrency Library. (Hozzáférés: 2012)
  48. Scala Joins
  49. (2007. június 1.) „Actors that unify threads and events”, Kiadó: In Proc. COORDINATION, LNCS.  
  50. Scala Join
  51. Chymyst
  52. Scala Joins. [2016. március 3-i dátummal az eredetiből archiválva]. (Hozzáférés: 2017. július 7.)
  53. Join Language

Források szerkesztés

Fordítás szerkesztés

Ez a szócikk részben vagy egészben a Join-pattern című angol Wikipédia-szócikk fordításán alapul. Az eredeti cikk szerkesztőit annak laptörténete sorolja fel. Ez a jelzés csupán a megfogalmazás eredetét és a szerzői jogokat jelzi, nem szolgál a cikkben szereplő információk forrásmegjelöléseként.