使用promise,編譯時需要 -std=c++11 -pthread
才會動。
就像其他語言一樣,promise主要用來做異步流程控制。
#include <future>
#include <thread>
#include <iostream>
using namespace std;
void runner(promise<int> *p) {
cout << "do some io task" << endl;
p->set_value(3);
}
int main() {
promise<int> p;
std::thread t(runner, &p);
int ret = p.get_future().get(); // Wait p.set_value()
cout << ret << endl;
t.join();
return 0;
}
do some io task
3
比較常使用到promise的狀況是跟其他library接的時候,對方透過callback或listener,
將執行完成的結果回傳,這時候就可以包裝變成同步的寫法。
#include <future>
#include <thread>
#include <string>
#include <exception>
#include <iostream>
using namespace std;
// Someone provides Downloader class.
class Downloader {
public:
void SetCallback(function<void(int,string)> cb) { cb_ = cb; }
void Download(string url) {
thread([=] () {
cout << "downloading: " << url << endl;
cb_(0, "ok");
}).detach();
}
private:
function<void(int,string)> cb_;
};
void download_callback(promise<string> *p, int status, string data) {
if(status != 0) {
p->set_exception(make_exception_ptr(runtime_error("download failed")));
} else {
p->set_value(data);
}
}
int main() {
promise<string> p;
Downloader d;
d.SetCallback(bind(download_callback, &p,
placeholders::_1, placeholders::_2));
d.Download("127.0.0.1");
try {
string ret = p.get_future().get();
cout << ret << endl;
} catch(exception &e) {
cout << "future: " << e.what() << endl;
}
return 0;
}
downloading: 127.0.0.1
ok
promise也有一些限制:它只能被使用一次,每次使用後就要宣告新的promise object。
如果使用condition variable的話就可以重複使用 wait()
和 notify_one()
,
但是必須自己處理value的回傳。換句話說,每次呼叫function都需要一個獨立的promise object。
如果上面的Download()連續呼叫兩次,程式就會發生錯誤。
所以Downloader內部還需要一個mutex防止被重複呼叫,才能正確運作。
一個比較好的寫法是從Downloader直接提供future物件給caller等待。
#include <future>
#include <unistd.h>
#include <thread>
#include <string>
#include <exception>
#include <iostream>
#include <chrono>
using namespace std;
class Downloader {
public:
future<string> Download(string url) {
auto p = make_shared<promise<string>>();
thread([=] () {
cout << "downloading: " << url << endl;
sleep(1);
download_callback(p.get(), 0, "ok");
}).detach();
return p->get_future();
}
private:
void download_callback(promise<string> *p, int status, string data) {
if(status != 0) {
p->set_exception(make_exception_ptr(runtime_error("download failed")));
} else {
p->set_value(data);
}
}
};
int main() {
Downloader d;
try {
auto now = time(NULL);
auto d1 = d.Download("127.0.0.1");
auto d2 = d.Download("127.0.0.2");
auto d3 = d.Download("127.0.0.3");
cout << d1.get() << endl;
cout << d2.get() << endl;
cout << d3.get() << endl;
cout << time(NULL) - now << endl;
} catch(exception &e) {
cout << "future: " << e.what() << endl;
}
return 0;
}
downloading: 127.0.0.3
downloading: 127.0.0.2
downloading: 127.0.0.1
ok
ok
ok
1