A számítástechnikában a konstrukciók futureés néhány programozási nyelvben a párhuzamos számításokhoz használt értékelési stratégiát alkotják . Segítségükkel leírnak egy olyan objektumot, amelyhez olyan eredményhez lehet hozzáférni, amelynek számítása jelenleg nem biztos, hogy befejeződik. promisedelay
Az ígéret kifejezést 1976-ban Daniel Friedman és David Wise [1] alkotta meg , Peter Hibbard pedig eventual -nak nevezte . [2] A jövőnek nevezett hasonló koncepciót Henry Baker és Carl Hewitt 1977-es tanulmánya javasolta. [3]
A jövő , ígéret és késleltetés kifejezéseket gyakran felcserélhetően használják, de a jövő és az ígéret közötti különbséget az alábbiakban ismertetjük . A Future általában egy változó írásvédett reprezentációja, míg az ígéret egy változtatható, egyetlen hozzárendelésű tároló , amely átadja a jövő értékét . [4] A Future meghatározható anélkül, hogy meghatároznánk, melyik ígéretből származik az érték. Ezenkívül több ígéret is társítható egyetlen jövőhöz , de csak egy ígéret rendelhet értéket egy jövőhöz. Ellenkező esetben a jövő és az ígéret együtt jön létre, és egymáshoz kötődik: a jövő érték, az ígéret pedig egy értéket kiadó függvény. A gyakorlatban a jövő egy aszinkron ígéretfüggvény visszatérési értéke . A jövőbeli érték hozzárendelésének folyamatát feloldásnak , teljesítésnek vagy kötésnek nevezik .
Egyes orosz nyelvű források a következő kifejezéseket használják: for future - future results [5] , futures [6] [7] [8] ; ígéretnek ígéret [9] [5] ; késedelemre — késedelem.
Megjegyzendő, hogy a megszámlálhatatlan (" jövendő ") és kétszavas (" jövő érték ") fordítások alkalmazhatósága nagyon korlátozott (lásd a vitát ). Konkrétan, az Alice ML nyelvfutures első osztályú tulajdonságokat biztosít, beleértve az első futures osztályú szintű ML modulokat – future modulesés future type modules[10] –, és mindezek a kifejezések lefordíthatatlannak bizonyulnak ezen változatok használatával. A kifejezés lehetséges fordítása ebben az esetben a „ jövő ” kifejezés – rendre „ első osztályú határidős ”, „ modulszintű határidős ”, „ jövőbeli struktúrák ” és „ jövő aláírások ” kifejezések csoportját adja. A " perspektíva " szabad fordítása lehetséges, a megfelelő terminológiai tartománnyal.
A jövő használata lehet implicit (bármilyen jövőre való hivatkozás az értékre való hivatkozást ad vissza) vagy explicit (a felhasználónak meg kell hívnia egy függvényt, hogy megkapja az értéket). Példa erre egy osztály getjava.util.concurrent.Future metódusa a Java nyelvben . Az explicit jövőből származó érték megszerzését csípésnek vagy kényszerítésnek nevezzük . Az explicit jövőket könyvtárként is meg lehet valósítani, míg az implicit jövőket általában a nyelv részeként valósítják meg.
Baker és Hewitt cikke leírja az implicit jövőket, amelyeket természetesen támogat a szereplő számítási modellje és a tisztán objektum-orientált nyelvek, mint például a Smalltalk . Friedman és Wise cikke csak az explicit határidőket írja le, valószínűleg az implicit határidők hagyományos számítógépeken való megvalósításának nehézségei miatt. A nehézség abban rejlik, hogy hardver szinten nem lehet majd olyan primitív adattípusként dolgozni a jövővel, mint az egész számok. Például az append utasítás használatával nem lesz képes feldolgozni a 3+ future factorial(100000) értéket . A tisztán objektumnyelveken és az aktormodellt támogató nyelveken ez a probléma megoldható a jövőbeli faktoriális(100000) +[3] üzenet elküldésével , amelyben a jövő azt mondja, hogy adjon hozzá 3 -at , és adja vissza az eredményt. Érdemes megjegyezni, hogy az üzenetátadási megközelítés attól függetlenül működik, hogy mennyi ideig tart a faktoriális (100000) kiszámítása, és nem igényel szúrást vagy kényszerítést.
A jövő használatakor az elosztott rendszerek késése jelentősen csökken . Például határidős ügyletekkel létrehozhat egy folyamatot az ígéretből [11] [12] , amely olyan nyelveken van megvalósítva, mint az E és a Joule , valamint az Argusban call-stream néven .
Vegyünk egy kifejezést hagyományos távoli eljáráshívásokkal :
t3 := ( xa() ).c( yb() )amely úgy tárható fel
t1 := xa(); t2 := yb(); t3 = t1.c(t2);Minden nyilatkozatban először üzenetet kell küldenie, és választ kell kapnia rá, mielőtt folytatná a következőt. Tegyük fel, hogy x , y , t1 és t2 ugyanazon a távoli gépen vannak. Ebben az esetben a harmadik állítás teljesítéséhez először két adatátvitelt kell végrehajtania a hálózaton keresztül. Ekkor a harmadik utasítás újabb adatátvitelt hajt végre ugyanarra a távoli gépre.
Ez a kifejezés a future használatával átírható
t3 := (x <- a()) <- c(y <- b())és mint
t1 := x <- a(); t2 := y <- b(); t3:= t1 <- c(t2);Ez az E nyelv szintaxisát használja, ahol az x <- a() jelentése "aszinkron módon továbbítja az a() üzenetet x -be ". Mindhárom változó jövőbelivé válik, és a program végrehajtása folytatódik. Később, amikor megpróbáljuk megszerezni a t3 értékét , késés léphet fel; csővezeték használatával azonban ez csökkenthető. Ha az előző példához hasonlóan x , y , t1 és t2 ugyanazon a távoli gépen található, akkor lehetséges a t3 kiszámítása egy csővezeték és egy hálózaton keresztüli adatátvitel segítségével. Mivel mindhárom üzenet ugyanazon a távoli gépen található változókra vonatkozik, csak egy kérést kell végrehajtania, és egy választ kell kapnia az eredmény eléréséhez. Ne feledje, hogy a t1 <- c(t2) átvitel akkor sem blokkolódik, ha t1 és t2 különböző gépeken vannak egymástól, vagy x és y között .
A csővezeték és az ígéret használatát meg kell különböztetni az üzenet párhuzamos, aszinkron továbbításától. Azon rendszereken, amelyek támogatják a párhuzamos üzenettovábbítást, de nem támogatják a csővezetékeket, a példából származó x <- a() és y <- b() üzenetek párhuzamosan is elküldhetők, de a t1 <- c(t2) elküldése szükséges. várja meg, amíg t1 és t2 érkezik , még akkor is, ha x , y , t1 és t2 ugyanazon a távoli gépen van. A csővezeték használatának késleltetési előnye még jelentősebbé válik olyan összetett helyzetekben, amikor több üzenetet kell küldeni.
Fontos, hogy ne keverjük össze az ígéretfolyamatot az üzenetfolyamattal a szereplőrendszerekben, ahol lehetőség van arra, hogy egy szereplő meghatározza és megkezdje a következő üzenet viselkedését, mielőtt az előző befejezte volna a feldolgozást.
Egyes programozási nyelvekben, mint például az Oz , az E és az AmbientTalk , lehetőség van a jövő megváltoztathatatlan ábrázolására, amely lehetővé teszi, hogy a feloldás után megkapja az értékét, de nem teszi lehetővé a feloldást:
A megváltoztathatatlan ábrázolások támogatása összhangban van a legkisebb privilégium elvével , mivel egy értékhez való hozzáférés csak azoknak az objektumoknak biztosítható, amelyeknek szükségük van rá. A csővezetékeket támogató rendszerekben az aszinkron üzenet küldője (eredménnyel) megváltoztathatatlan ígéretet kap az eredményről, az üzenet fogadója pedig a feloldó.
Egyes nyelveken, például az Alice ML -ben, a határidős ügyletek egy adott szálhoz vannak kötve, amely egy értéket értékel. Az értékelés azonnal elkezdődhet a jövő megteremtésekor, vagy lustán , azaz szükség szerint. A "lusta" jövő olyan, mint egy ütés (a lusta értékelés szempontjából).
Az Alice ML támogatja a határidős ügyleteket is, amelyek bármilyen szálon keresztül megoldhatók, és ott ígéretnek is nevezik . [14] Érdemes megjegyezni, hogy ebben az összefüggésben az ígéret nem ugyanazt jelenti, mint a fenti E példa : Alice ígérete nem változtathatatlan reprezentáció, és Alice nem támogatja az ígéretekből való kivezetést. De a csővezetékek természetesen együttműködnek a határidős ügyletekkel (beleértve az ígéretekhez kötötteket is).
Ha egy jövőbeli értéket aszinkron módon érünk el, például üzenetet adunk át neki, vagy várunk egy whenE-beli konstrukcióval, akkor nem nehéz megvárni, hogy a jövő megoldódjon az üzenet fogadása előtt. Ez az egyetlen dolog, amit figyelembe kell venni a tisztán aszinkron rendszerekben, például a színészmodellel rendelkező nyelveknél.
Egyes rendszereken azonban lehetőség van a jövőbeli érték azonnali és szinkron elérésére . Ez a következő módokon érhető el:
Az első mód például a C++11 -ben van megvalósítva , ahol az a szál, amelyben a jövőbeli értéket szeretné megkapni, blokkolható, amíg a wait()vagy a tag függvényei nem lesznek get(). A wait_for()vagy wait_until()a segítségével kifejezetten megadhat egy időtúllépést az örökös blokkolás elkerülése érdekében. Ha a jövőt a végrehajtás eredményeként kapjuk meg std::async, akkor a várakozási szálon blokkoló várakozással (nincs időtúllépés) a függvény végrehajtásának eredménye szinkronban fogadható.
Az I változó (az Id nyelvben ) egy jövő a fent leírt blokkoló szemantikával. Az I-struktúra egy I-változókból álló adatstruktúra . A szinkronizáláshoz használt hasonló konstrukciót, amelyben egy érték többször is hozzárendelhető, M-változónak nevezzük . Az M-változók támogatják a változó értékének megszerzésének és írásának atomi műveleteit, ahol az érték megszerzése az M-változót üres állapotba hozza vissza. [17]
A párhuzamos logikai változó hasonló a jövőhöz, de az egyesítés során ugyanúgy frissül, mint a logikai programozás logikai változói . Ezért több egységes értékkel is társítható (de nem térhet vissza üres vagy feloldatlan állapotba). Az Oz szálváltozói úgy működnek, mint a párhuzamos Boole-változók a fent leírt blokkoló szemantikával.
A kényszerű párhuzamos változó párhuzamos logikai változók általánosítása, amely támogatja a korlátozott logikai programozást : a megszorítás többször is szűkítheti a megengedett értékek készletét. Általában van mód egy thunk megadására, amely minden szűkítésnél végrehajtódik; ez szükséges a kényszer terjedésének támogatásához .
Az erősen kiszámított, szálspecifikus határidős ügyletek közvetlenül megvalósíthatók nem szálspecifikus határidős ügyletek formájában, ha létrehoznak egy szálat, amely a jövő létrehozásának időpontjában értékeli az értéket. Ebben az esetben kívánatos csak olvasható nézetet visszaadni a kliensnek, hogy csak a létrehozott szál tudja végrehajtani a jövőt.
Az implicit lusta szál-specifikus határidők (például az Alice ML-ben) nem szál-specifikus határidős határidők megvalósításához olyan mechanizmusra van szükség, amely meghatározza a jövőérték első felhasználási pontját (például a WaitNeeded konstrukciót Oz-ban [18] ). Ha minden érték objektum, akkor az érték továbbításához elegendő transzparens objektumok megvalósítása, mivel az első üzenet a továbbító objektumnak azt jelzi, hogy a jövő értékét ki kell értékelni.
A nem szál-specifikus határidős ügyletek szálspecifikus határidős ügyletekkel valósíthatók meg, feltéve, hogy a rendszer támogatja az üzenettovábbítást. A jövőbeli értéket igénylő szál üzenetet küldhet a jövőbeli szálnak. Ez a megközelítés azonban redundáns összetettséget vezet be. A szál alapú programozási nyelvekben a legkifejezőbb megközelítés valószínűleg a nem szál-specifikus határidők, csak olvasható nézetek és a „WaitNeeded” konstrukció vagy az átlátható továbbítás támogatásának kombinációja.
A „ jövő hívása ” értékelési stratégia nem determinisztikus: a jövő értékét valamikor a létrehozás után, de használat előtt értékelik. Az értékelés azonnal elkezdődhet a jövő megteremtése után (" buzgó értékelés "), vagy csak abban a pillanatban, amikor az értékre szükség van ( lusta értékelés , halasztott értékelés). A jövő eredményének értékelése után a következő hívások nem számítanak újra. Így a jövő igény szerinti hívást és memoizációt is biztosít .
A lusta jövő fogalma determinisztikus lusta kiértékelési szemantikát biztosít: a jövő érték kiértékelése az érték első felhasználásakor kezdődik, mint a "szükség szerint hívás" módszernél. A lusta jövők hasznosak olyan programozási nyelvekben, amelyek nem biztosítanak lusta értékelést. Például a C++11 nyelvben hasonló konstrukció hozható létre úgy, hogy megad egy indítási szabályzatot std::launch::sync, std::asyncés ad át egy függvényt, amely kiértékeli az értéket.
A szereplő modellben a forma egy kifejezése ''future'' <Expression>válaszként van definiálva egy Eval -üzenetre az E környezetben a C fogyasztó számára , a következőképpen: Egy jövőbeli kifejezés úgy válaszol egy Eval üzenetre, hogy elküldi C fogyasztónak az újonnan létrehozott F szereplőt (proxy a válasz kiértékeléssel <Expression>) visszatérési értékként, egyidejűleg az Eval<Expression> üzenetek kifejezés elküldésével az E környezetben a C fogyasztó számára . F viselkedését a következőképpen határozzuk meg:
A jövő egyes megvalósításai eltérően kezelhetik a kéréseket a párhuzamosság mértékének növelése érdekében. Például az 1 + jövőbeli faktoriális(n) kifejezés létrehozhat egy új jövőt, amely úgy viselkedik, mint az 1+faktoriál(n) .
A jövő - és ígéret - konstrukciókat először a MultiLisp és az Act 1 programozási nyelveken valósították meg . A logikai változók interakciós használata párhuzamos logikai programozási nyelvekben meglehetősen hasonló a jövőhöz. Köztük van a Prolog with Freeze és az IC Prolog , egy teljes értékű versenyképes primitívet valósított meg a Relational Language , a Concurrent Prolog , a Guarded Horn Clauses (GHC), a Parlog , a Strand , a Vulcan , a Janus , a Mozart / Oz , a Flow Java és az Alice ML . Az eredetileg az Id -ben bevezetett és a Reppy Concurrent ML -ben szereplő adatfolyam - programozási nyelvekből származó egyetlen I-var hozzárendelések hasonlóak a párhuzamos logikai változókhoz.
Barbara Liskov és Liuba Shrira 1988-ban [19] , egymástól függetlenül Mark S. Miller , Dean Tribble és Rob Jellinghaus javasolta a határidős ügyleteket a késések leküzdésére használó ígéret-csővezeték technikát a Project Xanadu részeként [20] 1989 körül .
Az ígéret kifejezést Liskov és Shrira alkotta meg, bár a csővezeték-mechanizmust call- streamnek nevezték (ma már ritkán használják).
Mindkét munkában és az ígéretfolyamat Xanadu implementációjában az ígéretek nem voltak első osztályú objektumok : a függvényargumentumok és a visszatérési értékek nem lehetnek közvetlenül ígéretek (ami bonyolítja a folyamat megvalósítását, például Xanaduban). Az ígéret és a hívásfolyam nem valósult meg az Argus [21] (a Liskov és Shrira munkájában használt programozási nyelv) nyilvános verzióiban; Az Argus 1988-ban leállította a fejlesztést. [22] A csővezeték megvalósítása Xanaduban csak az Udanax Gold [23] 1999-es kiadásával vált elérhetővé , és a közzétett dokumentáció nem magyarázza meg. [24]
A Joule és E Promise megvalósítások első osztályú objektumként támogatják őket.
Számos korai Actor nyelv, köztük az Act nyelvek, [25] [26] támogatta a párhuzamos üzenettovábbítást és üzenettovábbítást, de az ígéret-folyamatot nem. (Annak ellenére, hogy az ígéret-folyamat támogatott konstrukciókon keresztül valósítható meg, nincs bizonyíték az ilyen implementációkra az Act nyelvein.)
A Jövő koncepciója csatornákon keresztül is megvalósítható : a jövő egy szimpla csatorna, az ígéret pedig olyan folyamat, amely a jövő végrehajtásával értéket küld a csatornának [27] . Így valósulnak meg a határidős ügyletek olyan párhuzamos csatorna-kompatibilis nyelveken, mint a CSP és a Go . Az általuk megvalósított határidők explicitek, mert egy csatornából való kiolvasással érhetők el, nem pedig a normál kifejezés kiértékelésével.