2014-01-07 73 views
12

我一直在寫一些JavaScript,而我喜歡的關於環境的少數幾件事情之一就是它使用promises/futures來爲異步事件製作處理程序。C++期貨/承諾像JavaScript?

在C++中,你必須調用.get一個未來,它會阻塞,直到未來的結果可用,但在Javascript中,你可以編寫.then(fn),並在結果準備好時調用函數。至關重要的是,它在稍後的調用者的同一線程中執行此操作,所以不需要擔心線程同步問題,至少與C++中的問題不一樣。

我想在C++類似 -

auto fut = asyncImageLoader("cat.jpg"); 
fut.then([](Image img) { std::cout << "Image is now loaded\n" << image; }); 

有沒有辦法在C來實現這一++?顯然它需要某種事件隊列和事件循環來處理調用回調。我最終可能會編寫代碼來完成大部分工作,但希望看看是否有任何方法可以使用標準工具輕鬆實現目標。

+1

不完全重複,但密切相關:http://stackoverflow.com/questions/14489935/implementing-futurethen-equivalent-for-asynchronous-execution-in-c11 –

+0

對,不是很重複,但有趣的是: )出於某種原因,我的搜索沒有找到那個。 – jcoder

+1

「重要的是它[javascript]在同一個線程中執行此操作」。通常涉及兩個線程; (i)創建延遲的線程和附加的異步處理程序,(ii)延遲被執行或拒絕的線程以及異步處理程序被執行。鑑於(a)處理者不一定需要與延期創建時相同的線程中,(b)進度事件也可以被處理,並且(c)被延遲的人可以在其完成/被拒絕後繼續生存 - 那麼任何數量的更多線程也可能被涉及。 –

回答

9

A .then對於即將到來的C++ 17標準,std::future的功能已經爲proposed。 (它符合當前標準,但提供了附加功能作爲擴展)已經在新版本(1.53或更新版本)中提供了該功能的部分功能。

對於更爲完善的解決方案,請看Boost.Asio library,它確實允許輕鬆實現由future.then提供的異步控制流。 Asio的概念稍微複雜一些,因爲它需要訪問用於調度異步回調的中央對象io_service,並且需要手動管理工作線程。但原則上,這與您所要求的內容非常吻合。

+1

對,我用過阿西奧之前。但在我知道未來之前...... 看起來,這種組合和助推器的未來正是我所需要的。大。謝謝! – jcoder

2

您可以將一個例如實現Runnable類的對象傳遞給Future類的「then」方法。一旦未來完成其工作,請調用傳遞對象的「運行」方法。

+1

這會在做過這個工作的線程中調用回調,我希望它在「主」線程中調用它。部分原因是爲了解決同步問題。 – jcoder

+1

問題是你的主線程不能簡單地「等待」另一個線程來完成它的工作..你將不得不使用類似fork-join-framework的東西。 – maxdev

+0

這就是爲什麼我提到如果我要實現這個,我需要主線程來運行某種事件調度循環。我希望完成的期貨以某種方式排隊等待發貨....我可以寫這個,我真的只是想知道如果我能夠以更好的方式實現相同的目標,而無需編寫大量的代碼:) – jcoder

4

雖然建議使用then,但您可以通過指定的操作員技術實現自己的中綴then

創建一個struct then_t {};和一個static then_t then;。現在覆蓋左側和右側operator*,以便std::future<bool> *then* lambda創建一個std::async,它等待future,並將結果傳遞給lambda,然後返回lambda的返回值。

這需要大量的關注和注意,因爲您必須小心地創建副本以避免懸掛引用,並且會使用r和l值語法來使其充分高效。

你得到最終的語法是:

aut fut = asyncLoader("cat.jpg"); 
fut *then* [&](Image img) { std::cout << "Image loaded: " << img; }; 

這是非常接近你想要什麼。

如果你真聰明,你甚至可以把它也支持:

aut fut = asyncLoader("cat.jpg"); 
fut *then* [=] { std::cout << "Image loaded: " << fut.get(); }; 

其擺脫一些樣板和有時會是有用的。這要求asyncLoader返回std::shared_future而不是future

+0

我的問題是關於回調的線程。如果asyncLoader在第二個線程上加載我的圖像並完成未來,那麼我需要在原始線程的上下文中進行回調。 (這就是爲什麼我需要該線程中的某種disptach和事件循環)。看起來我可以使用asio編寫代碼來實現我想要的東西,即使不是那種確切的語法。 – jcoder

+0

@jcoder同意,原始線程上的東西很好。要做到這一點,你需要編寫一個增強的'future',其中包括編寫一個增強的'async'。這種增加的期貨將允許'then'將一個新任務注入到執行未來任務的線程的任務隊列中。它應該可以在C++ 11線程原語中使用;打包的任務,條件變量等 – Yakk

2

我不喜歡C++的未來,所以我寫了一個承諾庫,JavaScript在這裏會 https://github.com/xhawk18/promise-cpp

Defer newDelay(uint64_t timeout) { 
return newPromise([timeout](Defer d) { 
    setTimeout([d]() { 
     d->resolve(); 
    }, timeout); 
}); 

int main() { 
    uv_loop_t *loop = uv_default_loop(); 

    newPromise([](Defer d) { 
     setTimeout([d]() { 
      printf("In timerout 1\n"); 
      d.resolve(893); 
     }, 1000); 
    }).then([](int vv) { 
     printf("In then 1, vv = %d\n", vv); 
     return newDelay(1000); 
    }).then([]() { 
     printf("In then 2\n"); 
     return newDelay(2000); 
    }).then([]() { 
     printf("In then 3\n"); 
     return newDelay(3000); 
    }).then([]() { 
     printf("In last then\n"); 
    }); 

    return uv_run(loop, UV_RUN_DEFAULT); 
} 

使用Javascript的承諾相比,只是 -

  1. 使用newPromise,而不是JS的新Promise
  2. 使用lambda代替js函數
  3. 使用d.resolve代替js的解析
  4. 使用d.reject而不是JS的的拒絕

可以解決/拒絕與任何類型的paramters的,也無需在C關心麻煩的<> ++模板。