Future與promise

在計算機科學中，future、promise、delay和deferred是指用於在某些併發程式語言中同步程式執行的構造。由於某些計算。“promise”一詞由丹尼爾·福瑞得曼和David Wise在1976年提出，Peter Hibbard稱之為“eventual”。1977年Henry Baker和Carl Hewitt在一篇論文中介紹了一個類似的概念“future”。術語“future”、“promise”、“delay”和“deferred”通常可以互換使用，而“future”與“promise”之間的使用差異，我們將在下面討論。具體來說，當區分使用時，future是變數的“只讀”佔位符檢視，而promise是可寫的單賦值容器，用於設定future的值。 值得注意的是，無須指定可以設定其值的promise就可以定義future，並且不同的promise可以設定同一個future的值，儘管對於給定的future僅可以執行一次。在其他情況下，future和promise是一起建立的，並且相互關聯：future是值，promise是設定值的函式——本質上是非同步函式它。應用.future和promise起源於函數語言程式設計和相關範例分離，從而允許更靈活地進行計算，特別是透過並行化。後來，它在分散式計算中得到了應用 ，減少了通訊往返的延遲。再後來，它變得更有用了，因為它允許以直接風格編寫非同步程式，而不是以的方式。隱式與顯式.對future的使用可能是“隱式的”。獲得一個顯式的future的值可以稱為“stinging”或“forcing”。顯式future可以作為庫來實現，而隱式future則通常作為語言的一部分來實現。最初的Baker和Hewitt論文描述了隱式future，它們在演員模型和純面向物件程式語言的future。例如，add指令不會處理codice_2 。在純演員模型或面嚮物件語言中，這個問題可以透過傳送codice_3訊息codice_4，它要求future自己加codice_5並返回結果。請注意，無論codice_6何時完成計算，訊息傳遞方法都可以工作，而且不需要任何sting或force。promise流水線.在分散式系統中使用future可以顯著地減少延遲。例如，future讓“promise流水線”成為了可能， 就像在E語言和Joule語言中實現的那樣，在Argus語言中這也被稱為“call-stream”。考慮一個涉及常規遠端過程呼叫的表示式，例如：可以擴充套件為每個語句需要傳送一條訊息，並在下一個語句可以繼續之前收到一個答覆。例如，假設codice_7、codice_8、codice_9和codice_10都位於同一臺遠端機器上。在這種情況下，在開始執行第三條語句之前，必須對該機器進行兩次完整的網路往返。然後，第三條語句將引起另一個到同一個遠端機器的往返。使用future，上面的表示式可以寫成可以擴充套件為這裡使用的語法是E語言的語法，其中codice_11表示將訊息codice_12非同步傳送給codice_7。所有三個變數都會立即為其結果分配future，執行過程將繼續進行到後面的語句。之後嘗試解決codice_14的值可能會導致延遲；但是，流水線操作可以減少所需的往返次數。如果與前面的示例一樣，codice_7、codice_8、codice_9和codice_10都位於相同的遠端機器上，則流水線實現可以用一次往返來計算codice_14，不必用三次。由於所有三條訊息都指向同一遠端計算機上的物件，因此只需要傳送一個請求，只需要接收一個包含結果的響應。另請注意，即使codice_9和codice_10位於不同機器上，或者位於與codice_7或codice_8不同的機器上，傳送codice_24也不會阻塞。promise流水線應與並行非同步訊息傳遞區分開來。在支援並行訊息傳遞但不支援流水線操作的系統中，上面示例中的訊息傳送codice_11和codice_26可以並行進行，但傳送codice_24將不得不等到codice_9和codice_10都被接收，即使codice_7、codice_8、codice_9和codice_10在同一個遠端機器上。在涉及許多訊息的更復雜情況下，流水線的相對延遲優勢變得更大。promise流水線操作也不應與演員系統中的流水線訊息處理相混淆，在這種系統中，可以演員在完成當前訊息的處理之前指定並開始執行下一個訊息的行為。只讀檢視.在某些程式語言中 ，可以獲得未來的“只讀檢視”，該檢視允許在resolve後讀取其值，但不允許resolve它：!!運算子用於獲得只讀檢視對只讀檢視的支援符合最小特權原則，因為它允許將值設定為僅限於需要設定該值的主體。在同樣支援流水線的系統中，非同步訊息的傳送方接收結果的只讀承諾promise，訊息的目標接收resolver。針對特定執行緒的future.某些語言定義了與計算future值的特定執行緒相關聯的future。 這種計算可以在建立future時及早開始，或者在首次需要其值時懶惰地開始。在延遲計算的意義上，懶惰的future類似於thunk 。Alice ML還支援可由任何執行緒解決的future，並呼叫這些“promise”。 “promise”的這種使用不同於上文所述的在E語言中的使用。在Alice中，promise不是隻讀檢視，並且不支援promise流水線操作。相反，對於future未來，包括與promise相關的future，流水線是自然而然地發生的。阻塞與非阻塞語義.如果future的值是非同步訪問的，例如透過向它傳送訊息，或者透過使用類似於E語言中的codice_41的構造顯式地等待它，那麼在收到訊息或完成等待之前，推遲到future得到resolve沒有任何困難。這是在純非同步系統中唯一需要考慮的情況。然而，在某些系統中，還可能嘗試“立即”或“同步”訪問future的值。這樣的話就需要做出一個設計選擇：作為第一種可能性的示例，在C++11中 ，需要future值的執行緒可以透過呼叫codice_42或codice_43成員函式來阻塞，直到可用為止。您還可以使用codice_44或codice_45成員函式指定等待超時，以避免無限期阻塞。如果future對codice_38的呼叫，那麼阻塞等待可能導致函式的同步呼叫以計算等待執行緒上的結果。相關結構.“future”是Event的特例，只能完成一次。通常，event可以重置為初始空狀態，因此可以根據需要多次完成。“I-var”是具有上面定義的阻塞語義的future。“I-structure”是包含I-var的資料結構。可以使用不同值多次設定的相關同步構造稱為“M-var”。M-var支援“採用”或“放置”當前值的原子操作，其中取值還將M-var設定回其初始“空”狀態。“併發邏輯變數”與future類似，但是透過合一更新，與邏輯程式設計中的“邏輯變數”相同。因此，它可以多次繫結到可合一的值，但不能設定回空或unresolved狀態。Oz的資料流變數充當併發邏輯變數，並且還具有上面提到的阻塞語義。“併發約束變數”是併發邏輯變數的一般化，以支援約束邏輯程式設計：約束可以多次“縮小”，表示可能值的較小集合。通常，有一種方法可以指定每當約束進一步縮小時應該執行的hunk；這是支援“約束傳播”所必需的。不同形式future的表達能力之間的關係.透過在建立future的同時建立一個計算值的執行緒，可以在非執行緒特有的promise中直接實現及早求值的執行緒特有的future。在這種情況下，future將只讀檢視返回給客戶端，以便僅讓新建立的執行緒能夠解決這個future。要在非執行緒特有的promise中實現隱式延遲執行緒特有的promise。如果所有值都是物件，那麼實現透明轉發物件的能力就足夠了，因為傳送給轉發器的第一條訊息表明需要future的值。假定系統支援訊息傳遞，透過讓resolve執行緒向future自己的執行緒傳送訊息，可以線上程特有的future中實現非執行緒特有的future。但是，這可以被視為不必要的複雜性。在基於執行緒的程式語言中，最具表現力的方法似乎是提供非執行緒特有的future，只讀檢視以及“WaitNeeded”構造或支援透明轉發的混合。求值策略.future的求值策略，並且可以在中途暫停，或在一次執行中執行。一旦future被賦值，它就不會在訪問future的時候重新計算；這就像傳需求呼叫時使用的記憶化。懶惰future是確定性具有惰性求值評估語義的future：future值的計算在首次需要時開始，與傳需要呼叫一樣。懶惰future在求值策略預設不是懶惰求值的語言中使用。例如，在C++11中，可以透過將codice_48啟動策略傳遞給codice_38以及計算值的函式來建立這種惰性future。演員模型中的future語義.在演員模型中，形式為codice_50的表示式由它對codice_51訊息作為返回值，“與此同時"向codice_53傳送環境E和客戶C的codice_51訊息。F的預設行為如下：但是，一些future可以透過特殊方式處理請求以提供更大的並行性。例如，表示式codice_58可以建立一個新的future，其行為類似於數字codice_59 。這個技巧並不總是有效。例如，以下條件表示式：codice_60會掛起，直到codice_61這個future已回應詢問codice_62是否大於其自身的請求。歷史.“future”與“promise”構造首先在諸如MultiLisp和Act 1之類的程式語言中實現。在併發邏輯程式語言中使用邏輯變數進行通訊非常類似於future。這些開始於“Prolog with Freeze”和“IC Prolog”，併成為真正的併發原語，包括關係語言、Concurrent Prolog、守衛霍恩子句、Parlog、Strand、Vulcan、Janus、Oz-Mozart、Flow Java和Alice ML。來自資料流程式語言的單一賦值“I-var”，源自Id幷包含在Reppy的“Concurrent ML”中，非常類似於併發邏輯變數。promise流水線技術是Barbara Liskov和Liuba Shrira於1988年發明的，由Mark S. Miller、Dean Tribble和Rob Jellinghaus在大約1989年的Xanadu專案中獨立發明。“promise”一詞是由Liskov和Shrira創造的，儘管他們透過名稱“call-stream”引用了流水線機制，現在很少使用它。Liskov和Shrira的論文中描述的設計以及Xanadu中的promise流水線的實現都有一個限制，即promise值不是一等的：一個引數，或者一個call或send返回的值不能直接成為一個promise 。似乎promise和call-stream從未在Argus的任何公開發布中實現， Liskov和Shrira論文中使用的程式語言。Argus的開發在1988年左右停止了。 Xanadu實現的promise流水線僅在1999年Udanax Gold的原始碼釋出時才公開發布，並且在任何已釋出的文件中都沒有解釋過。 Joule和E的後續實現支援完全一等的promise和resolver。一些早期的演員語言，包括Act系列， 支援並行訊息傳遞和流水線訊息處理，但不支援promise流水線。2000年之後，由於訊息傳遞的請求-響應模型，future和promise在使用者介面響應和web開發中的應用重新引起了人們的興趣。現在有幾種主流語言對future和promise都有語言支援，最著名的是Java 5中的codice_63、Scala和C++的草案。實現列表.支援future、promise、併發邏輯變數、資料流變數或I-vars的語言，無論是透過直接語言支援還是在標準庫中，包括：還支援promise流水線的語言包括：基於非標準庫的future實現：協程.future可以用協程或生成器實現， 從而產生相同的評估策略。通道.future可以很容易地用通道實現：future是一個單元素的通道，而promise是一個傳送到通道，實現future的過程。 這允許future在支援通道的併發程式語言中實現。由此產生的future是顯式的，因為它們必須透過從通道讀取而不是僅僅透過評估來獲取。