Ve vědě o počítačích tvoří konstrukty futurea promisev delayněkterých programovacích jazycích strategii hodnocení používanou pro paralelní výpočty . S jejich pomocí je popsán objekt, ke kterému lze přistupovat pro výsledek, jehož výpočet nemusí být v tuto chvíli dokončen.
Termín slib byl vytvořen v roce 1976 Danielem Friedmanem a Davidem Wiseem [1] a Peter Hibbard jej nazval eventual . [2] Podobný koncept zvaný budoucnost navrhli v roce 1977 v článku Henry Baker a Carl Hewitt. [3]
Termíny budoucnost , slib a zpoždění se často používají zaměnitelně, ale rozdíl mezi budoucností a příslibem je popsán níže . Future je obvykle reprezentace proměnné pouze pro čtení , zatímco slib je měnitelný kontejner s jedním přiřazením , který předává hodnotu budoucnosti . [4] Budoucnost může být definována, aniž by bylo specifikováno, z jakého příslibu bude hodnota pocházet. Také více příslibů může být spojeno s jednou budoucností , ale pouze jeden příslib může přiřadit hodnotu budoucnosti. V opačném případě jsou budoucnost a příslib vytvořeny společně a vzájemně svázány: budoucnost je hodnota a příslib je funkce, která přiděluje hodnotu. V praxi je budoucnost návratovou hodnotou asynchronní funkce slibu . Proces přiřazení budoucí hodnoty se nazývá vyřešení , splnění nebo závaznost .
Některé zdroje v ruštině používají následující překlady termínů: for future - future results [5] , futures [6] [7] [8] ; za slib, slib [9] [5] ; za zpoždění — zpoždění.
Je třeba poznamenat, že nespočitatelné („ budoucí “) a dvouslovné („ budoucí hodnota “) překlady mají velmi omezenou použitelnost (viz diskuze ). Zejména jazyk Alice ML poskytuje futuresprvotřídní vlastnosti, včetně poskytování futures prvotřídních modulů ML - future modulesa future type modules[10] - a všechny tyto termíny se pomocí těchto variant ukážou jako nepřeložitelné. Možný překlad termínu se v tomto případě ukazuje jako „ budoucnost “ – respektive, dává skupinu termínů „ prvotřídní futures “, „ budoucnosti na úrovni modulu “, „ budoucí struktury “ a „ budoucí podpisy “. Je možný volný překlad „ perspektiva “ s odpovídajícím terminologickým rozsahem.
Použití budoucnosti může být implicitní (jakýkoli odkaz na budoucnost vrátí odkaz na hodnotu) nebo explicitní (uživatel musí zavolat funkci, aby získal hodnotu). Příkladem je metoda get třídy java.util.concurrent.Futurev jazyce Java . Získání hodnoty z explicitní budoucnosti se nazývá bodání nebo vynucování . Explicitní futures mohou být implementovány jako knihovna, zatímco implicitní futures jsou obvykle implementovány jako součást jazyka.
Bakerův a Hewittův článek popisuje implicitní budoucnosti, které jsou přirozeně podporovány ve výpočtovém modelu aktéra a čistě objektově orientovaných jazycích, jako je Smalltalk . Článek Friedmana a Wise popisuje pouze explicitní futures, s největší pravděpodobností kvůli obtížnosti implementace implicitních futures na konvenčních počítačích. Potíž spočívá v tom, že na hardwarové úrovni nebude možné pracovat s budoucností jako s primitivním datovým typem jako jsou celá čísla. Například pomocí příkazu append nebude možné zpracovat 3 + future factorial(100000) . V čistě objektových jazycích a jazycích, které podporují model aktéra, lze tento problém vyřešit odesláním zprávy future factorial(100000) +[3] , ve které bude budoucnosti řečeno, aby přidala 3 a vrátila výsledek. Stojí za zmínku, že přístup předávání zpráv funguje bez ohledu na to, jak dlouho trvá výpočet faktoriálu (100 000) a nevyžaduje bodání nebo vynucení.
Při použití budoucnosti se výrazně sníží zpoždění v distribuovaných systémech . Například pomocí futures můžete vytvořit kanál ze slibu [11] [12] , který je implementován v jazycích jako E a Joule , stejně jako v Argusu s názvem call-stream .
Zvažte výraz využívající tradiční volání vzdálených procedur :
t3 := ( xa()).c( yb())které lze odhalit jako
t1 := xa(); t2 := yb(); t3:= tl.c(t2);V každém výpisu musíte nejprve odeslat zprávu a obdržet na ni odpověď, než budete pokračovat v dalším. Předpokládejme, že x , y , t1 a t2 jsou na stejném vzdáleném počítači. V tomto případě k dokončení třetího tvrzení musíte nejprve provést dva přenosy dat po síti. Poté třetí příkaz provede další přenos dat na stejný vzdálený počítač.
Tento výraz lze přepsat pomocí future
t3 := (x <- a()) <- c(y <- b())a zveřejněny jako
t1 := x <- a(); t2 := y <- b(); t3:= t1 <- c(t2);Toto používá syntaxi z jazyka E, kde x <- a() znamená "asynchronně přeposílat zprávu a() do x ". Všechny tři proměnné se stanou budoucností a provádění programu pokračuje. Později, když se pokusíte získat hodnotu t3 , může dojít ke zpoždění; použití potrubí to však může snížit. Pokud jsou jako v předchozím příkladu x , y , t1 a t2 umístěny na stejném vzdáleném stroji, pak je možné realizovat výpočet t3 pomocí potrubí a jednoho přenosu dat po síti. Protože všechny tři zprávy jsou pro proměnné umístěné na stejném vzdáleném počítači, stačí provést jeden požadavek a získat jednu odpověď, abyste získali výsledek. Všimněte si, že přenos t1 <- c(t2) nebude blokován, i když t1 a t2 byly na různých počítačích od sebe navzájem nebo od x a y .
Použití kanálu ze slibu by se mělo odlišovat od předávání zprávy paralelně asynchronně. Na systémech, které podporují paralelní předávání zpráv, ale nepodporují kanály, lze posílání zpráv x <- a() a y <- b() z příkladu provést paralelně, ale odeslání t1 <- c(t2) bude muset počkejte, dokud nebude přijato t1 a t2 , i když x , y , t1 a t2 jsou na stejném vzdáleném počítači. Výhoda latence při použití kanálu se stává významnější ve složitých situacích, kdy je třeba odeslat více zpráv.
Je důležité nezaměňovat zprostředkování slibů s kanálem zpráv v systémech aktérů, kde je možné, aby aktér specifikoval a začal provádět chování pro další zprávu dříve, než ta předchozí dokončí zpracování.
V některých programovacích jazycích, jako je Oz , E a AmbientTalk , je možné získat neměnnou reprezentaci budoucnosti, která vám umožní získat její hodnotu po vyřešení, ale nedovolí vám vyřešit:
Podpora neměnných reprezentací je v souladu s principem nejmenšího privilegia , protože přístup k hodnotě může být udělen pouze těm objektům, které to potřebují. V systémech, které podporují pipeline, odesílatel asynchronní zprávy (s výsledkem) obdrží neměnný příslib výsledku a příjemce zprávy je resolver.
V některých jazycích, jako je Alice ML , jsou futures vázány na konkrétní vlákno, které vyhodnocuje hodnotu. Vyhodnocování může začít ihned při vytvoření budoucnosti, nebo líně , tedy podle potřeby. "Líná" budoucnost je jako thunk (ve smyslu líného hodnocení).
Alice ML také podporuje futures, které lze vyřešit libovolným vláknem a také se tomu tam říká slib . [14] Stojí za zmínku, že v tomto kontextu slib neznamená totéž jako v příkladu E výše : Alicin slib není neměnnou reprezentací a Alice nepodporuje falšování slibů. Potrubní rozvody ale přirozeně pracují s futures (včetně těch, které jsou vázané na sliby).
Pokud se k budoucí hodnotě přistupuje asynchronně, například předáním zprávy nebo čekáním pomocí konstruktu whenv E, pak není těžké před přijetím zprávy čekat na vyřešení budoucnosti. To je jediná věc, kterou je třeba vzít v úvahu v čistě asynchronních systémech, jako jsou jazyky s modelem herce.
Na některých systémech je však možné přistupovat k budoucí hodnotě okamžitě a synchronně . Toho lze dosáhnout následujícími způsoby:
První způsob je například implementován v C++11 , kde vlákno, ve kterém chcete získat budoucí hodnotu, může blokovat, dokud nebude člen fungovat wait()nebo get(). Pomocí wait_for()nebo wait_until()můžete explicitně určit časový limit, abyste se vyhnuli věčnému blokování. Pokud je budoucnost získána jako výsledek provedení std::async, pak s blokujícím čekáním (bez časového limitu) na čekajícím vláknu může být výsledek provedení funkce přijat synchronně.
Proměnná I (v jazyce Id ) je budoucností s výše popsanou blokovací sémantikou. I-struktura je datová struktura skládající se z I-proměnných. Podobný konstrukt používaný pro synchronizaci, ve kterém lze hodnotu přiřadit vícekrát, se nazývá M-proměnná . M-proměnné podporují atomické operace získávání a zápisu hodnoty proměnné, kde získání hodnoty vrací M-proměnnou do prázdného stavu. [17]
Paralelní booleovská proměnná je podobná budoucnosti, ale je aktualizována během sjednocení stejným způsobem jako booleovské proměnné v logickém programování . Proto může být spojena s více než jednou jednotnou hodnotou (ale nemůže se vrátit do prázdného nebo nevyřešeného stavu). Vláknové proměnné v Oz fungují jako souběžné booleovské proměnné s blokovací sémantikou popsanou výše.
Vázaná paralelní proměnná je zobecněním paralelních booleovských proměnných s podporou omezeného logického programování : omezení může množinu povolených hodnot několikrát zúžit . Obvykle existuje způsob, jak určit thunk, který bude proveden při každém zúžení; to je nezbytné pro podporu šíření omezení .
Silně vypočítané futures specifické pro vlákno lze implementovat přímo z hlediska futures nespecifických pro vlákno vytvořením vlákna pro vyhodnocení hodnoty v okamžiku vytvoření budoucnosti. V tomto případě je žádoucí vrátit klientovi pohled jen pro čtení, aby v budoucnu mohlo spouštět pouze vytvořené vlákno.
Implementace implicitních futures specifických pro líné vlákno (jako v Alice ML) z hlediska futures nespecifických pro vlákno vyžaduje mechanismus pro určení prvního bodu použití budoucí hodnoty (jako je konstrukt WaitNeeded v Oz [18] ). Pokud jsou všechny hodnoty objekty, pak stačí implementovat transparentní objekty pro předání hodnoty, protože první zpráva do objektu předávání bude znamenat, že musí být vyhodnocena hodnota budoucnosti.
Futures nespecifické pro vlákna lze implementovat prostřednictvím futures specifických pro vlákna za předpokladu, že systém podporuje předávání zpráv. Vlákno, které vyžaduje budoucí hodnotu, může odeslat zprávu budoucímu vláknu. Tento přístup však přináší nadbytečnou složitost. V programovacích jazycích založených na vláknech je pravděpodobně nejvýraznějším přístupem kombinace budoucnosti, která není specifická pro vlákna, pohledů pouze pro čtení a buď konstrukce 'WaitNeeded' nebo podpora transparentního předávání.
Strategie hodnocení „ volání podle budoucnosti “ není deterministická: hodnota budoucnosti bude vyhodnocena v určitém okamžiku po vytvoření, ale před použitím. Vyhodnocování může začít ihned po vytvoření budoucnosti (" dychtivé hodnocení "), nebo až v okamžiku, kdy je hodnota potřeba ( líné hodnocení , odložené hodnocení). Jakmile je výsledek budoucnosti vyhodnocen, následující hovory se nepřepočítávají. Budoucnost tedy poskytuje jak volání podle potřeby , tak memoování .
Koncept líné budoucnosti poskytuje deterministickou sémantiku líného hodnocení: ohodnocení budoucí hodnoty začíná při prvním použití hodnoty, jako v metodě „volání podle potřeby“. Líné budoucnosti jsou užitečné v programovacích jazycích, které neposkytují líné hodnocení. Například v C++11 lze podobnou konstrukci vytvořit zadáním zásady spouštění std::launch::syncpro std::asynca předáním funkce, která vyhodnocuje hodnotu.
V modelu Actor je výraz formuláře ''future'' <Expression>definován jako odpověď na zprávu Eval v prostředí E pro spotřebitele C takto: Budoucí výraz reaguje na zprávu Eval odesláním spotřebitele C nově vytvořeného aktéra F (zástupce pro odpověď s vyhodnocením <Expression>) jako návratová hodnota, zároveň s odesláním <Expression>zpráv výrazu Eval v prostředí E pro spotřebitele C . Chování F je definováno takto:
Některé implementace budoucnosti mohou zpracovávat požadavky odlišně, aby se zvýšila míra paralelismu. Například výraz 1 + faktor budoucnosti (n) může vytvořit novou budoucnost, která se chová jako číslo 1+faktoriál(n) .
Konstrukce budoucnosti a slibu byly poprvé implementovány v programovacích jazycích MultiLisp a Act 1 . Použití booleovských proměnných pro interakci v souběžných logických programovacích jazycích je docela podobné budoucnosti. Mezi nimi jsou Prolog with Freeze a IC Prolog , plnohodnotné kompetitivní primitivum implementovaly Relational Language , Concurrent Prolog , Guarded Horn Clauses (GHC), Parlog , Strand , Vulcan , Janus , Mozart / Oz , Flow Java a Alice ML . Jednotlivá přiřazení I-var z programovacích jazyků datových toků , původně zavedená v Id a zahrnutá v Reppy Concurrent ML , jsou podobná souběžným booleovským proměnným.
Techniku slibového potrubí využívající futures k překonání zpoždění navrhly Barbara Liskov a Liuba Shrira v roce 1988 [19] a nezávisle Mark S. Miller , Dean Tribble a Rob Jellinghaus jako součást projektu Xanadu kolem roku 1989 [20] .
Termín slib byl vytvořen Liskovem a Shrira, ačkoli oni nazvali mechanismus potrubí call-stream (nyní zřídka používaný).
V obou dílech a v implementaci kanálu slibů Xanadu nebyly sliby prvotřídními objekty : argumenty funkcí a návratové hodnoty nemohly být přímo sliby (což komplikuje implementaci potrubí, například v Xanadu). slib a call-stream nebyly implementovány ve veřejných verzích Argus [21] (programovací jazyk používaný v díle Liskova a Shrira); Argus zastavil vývoj v roce 1988. [22] Implementace potrubí v Xanadu byla dostupná až s vydáním Udanax Gold [23] v roce 1999 a není vysvětlena ve zveřejněné dokumentaci. [24]
Implementace Promise v Joule a E je podporují jako prvotřídní objekty.
Několik časných jazyků herců, včetně jazyků Act, [25] [26] podporovalo paralelní předávání zpráv a zřetězení zpráv, ale ne kanál slibů. (Navzdory možnosti implementace kanálu slibů prostřednictvím podporovaných konstrukcí neexistují žádné důkazy o takových implementacích v jazycích zákona.)
Koncept budoucnosti může být implementován z hlediska kanálů : budoucnost je singletonový kanál a příslib je proces, který posílá hodnotu kanálu provedením budoucnosti [27] . Takto jsou futures implementovány v souběžných jazycích s povoleným kanálem, jako je CSP a Go . Budoucnosti, které implementují, jsou explicitní, protože jsou přístupné čtením z kanálu, nikoli vyhodnocením normálního výrazu.