C++ 異步訪問

packaged_task

一個 packaged_task 包裝了一個可調用對象,並且允許異步獲取該可調用對象的結果

它重載了 operator() ,因此它也是一個可調用對象。

一個 packaged_task 對象包含兩個部分:

  • stored task:就是一個可調用對象。
  • shared state:用於存儲 stored task 的執行結果,且可以通過 future 異步訪問之。

調用 get_future 成員函數之後,返回的 future 便與 shared state 建立關聯。

shared state 會一直存在直到最後一個與之關聯的對象釋放了該關聯,或該對象被銷燬。

構造

default (1) packaged_task() noexcept;
initialization (2) template <class Fn> explicit packaged_task (Fn&& fn);
with allocator (3) template <class Fn, class Alloc> explicit packaged_task (allocator_arg_t aa, const Alloc& alloc, Fn&& fn);
copy [deleted] (4) packaged_task (const packaged_task&) = delete;
move (5) packaged_task (packaged_task&& x) noexcept;

如,

std::packaged_task<int(int)> bar ([](int x){return x*2;}); // initialized

賦值

move (1) packaged_task& operator= (packaged_task&& rhs) noexcept;
copy [deleted] (2) packaged_task& operator= (const packaged_task&) = delete;

shared state 相關操作

future<Ret> get_future();

獲取和 shared state 關聯的 future 。

bool valid() const noexcept;

如果 packaged_task 當前與一個 shared state 關聯,則返回 true 。

默認構造的 packaged_task 沒有關聯的 shared state 。

void reset();

讓 packaged_task 與一個新的 shared state 關聯。不改變 stored task 。

// packaged_task::get_future
#include <iostream>
#include <utility>
#include <future>
#include <thread>

// a simple task:
int triple (int x) { return x*3; }

int main () {
  std::packaged_task<int(int)> tsk (triple); // package task

  std::future<int> fut = tsk.get_future();   // get future

  std::thread(std::move(tsk),33).detach();   // spawn thread and call task

  // ...

  int value = fut.get();                     // wait for the task to complete and get result

  std::cout << "The triple of 33 is " << value << ".\n";

  return 0;
}
The triple of 33 is 99.
// packaged_task::reset
#include <iostream>
#include <utility>
#include <future>
#include <thread>

// a simple task:
int triple (int x) { return x*3; }

int main () {
  std::packaged_task<int(int)> tsk (triple); // package task

  std::future<int> fut = tsk.get_future();
  tsk(33);
  std::cout << "The triple of 33 is " << fut.get() << ".\n";

  // re-use same task object:
  tsk.reset();
  fut = tsk.get_future();
  std::thread(std::move(tsk),99).detach();
  std::cout << "Thre triple of 99 is " << fut.get() << ".\n";

  return 0;
}
The triple of 33 is 99.
The triple of 99 is 297.

其他操作

void operator()(Args... args);

使用 args 作爲參數調用 stored task 。

void make_ready_at_thread_exit (args... args);

使用 args 作爲參數調用 stored task ,但是讓 shared state 在線程退出時才就緒,而不是一完成調用就讓其就緒。

交換

成員函數 void swap (packaged_task& x) noexcept;
非成員函數 template <class Ret, class… Args> void swap (packaged_task<Ret(Args…)>& x, packaged_task<Ret(args…)>& y) noexcept;

交換兩者的 stored task 和 shared state 。

promise

可以往一個 promise 對象中存儲一個值,然後通過與其關聯的 future 對象來獲取該值。

一旦 promise 對象構造完成,它便關聯有一個 shared state,其用於存儲一個值或一個異常。

調用 get_future 成員函數之後,返回的 future 便與 shared state 建立關聯。

shared state 會一直存在直到最後一個與之關聯的對象釋放了該關聯,或該對象被銷燬。

構造

default (1) promise();
with allocator (2) template <class Alloc> promise (allocator_arg_t aa, const Alloc& alloc);
copy [deleted] (3) promise (const promise&) = delete;
move (4) promise (promise&& x) noexcept;

如,

std::promise<int> foo;
std::promise<int> bar = std::promise<int>(std::allocator_arg,std::allocator<int>());

賦值

move (1) promise& operator= (promise&& rhs) noexcept;
copy [deleted] (2) promise& operator= (const promise&) = delete;

shared state 相關操作

設置值

generic template (1) void set_value (const T& val);

void set_value (T&& val);
specializations (2) void promise<R&>::set_value (R& val); // when T is a reference type (R&)

void promise<void>::set_value (void); // when T is void
generic template (1) void set_value_at_thread_exit (const T& val);
<br.>void set_value_at_thread_exit (T&& val);
specializations (2) void promise<R&>::set_value_at_thread_exit (R& val); // when T is a reference type (R&)

void promise<void>::set_value_at_thread_exit (void); // when T is void

在線程退出時才讓 shared state 就緒。

設置異常

void set_exception (exception_ptr p);

調用 future::get() 函數之後,將拋出 p 指向的異常。

void set_exception_at_thread_exit (exception_ptr p);

在線程退出時才讓 shared state 就緒。

獲取關聯的 future

future<T> get_future();
// promise example
#include <iostream>
#include <functional>
#include <thread>
#include <future> 

void print_int (std::future<int>& fut) {
  int x = fut.get();
  std::cout << "value: " << x << '\n';
}

int main () {
  std::promise<int> prom;                      // create promise

  std::future<int> fut = prom.get_future();    // engagement with future

  std::thread th1 (print_int, std::ref(fut));  // send future to new thread

  prom.set_value (10);                         // fulfill promise
                                               // (synchronizes with getting the future)
  th1.join();
  return 0;
}
value: 10
// promise::set_exception
#include <iostream>
#include <functional>
#include <thread>
#include <future>
#include <exception>

void get_int (std::promise<int>& prom) {
  int x;
  std::cout << "Please, enter an integer value: ";
  std::cin.exceptions (std::ios::failbit);   // throw on failbit
  try {
    std::cin >> x;                           // sets failbit if input is not int
    prom.set_value(x);
  }
  catch (std::exception&) {
    prom.set_exception(std::current_exception());
  }
}

void print_int (std::future<int>& fut) {
  try {
    int x = fut.get();
    std::cout << "value: " << x << '\n';
  }
  catch (std::exception& e) {
    std::cout << "[exception caught: " << e.what() << "]\n";
  }
}

int main () {
  std::promise<int> prom;
  std::future<int> fut = prom.get_future();

  std::thread th1 (print_int, std::ref(fut));
  std::thread th2 (get_int, std::ref(prom));

  th1.join();
  th2.join();
  return 0;
}
Please enter an integer value: boogey!
[exception caught: ios_base::failbit caught]

其他操作

交換

成員函數 void swap (promise& x) noexcept;
非成員函數 template <class T> void swap (promise<T>& x, promise<T>& y) noexcept;

交換兩者的 shared state 。

async

用於異步調用一個可調用對象。

unspecified policy (1) template <class Fn, class... Args> future<typename result_of<Fn(Args...)>::type> async (Fn&& fn, Args&&... args);
specific policy (2) template <class Fn, class... Args> future<typename result_of<Fn(Args...)>::type> async (launch policy, Fn&& fn, Args&&... args);

在某個時間點調用 fn(args) 。

立即返回,不阻塞。

fn 的返回值通過 async 返回的 future 獲取。

形式(1)等同於使用 launch::async|launch::deferred 作爲 launce policy 。

policy 可取值如下:

policy description
launch::async Asynchronous: Launches a new thread to call fn (as if a thread object is constructed with fn and args as arguments, and accessing the shared state of the returned future joins it).
launch::deferred Deferred: The call to fn is deferred until the shared state of the returned future is accessed (with wait or get). At that point, fn is called and the function is no longer considered deferred. When this call returns, the shared state of the returned future is made ready.
launch::async|launch::deferred Automatic: The function chooses the policy automatically (at some point). This depends on the system and library implementation, which generally optimizes for the current availability of concurrency in the system.
// async example
#include <iostream>
#include <future>

// a non-optimized way of checking for prime numbers:
bool is_prime (int x) {
  std::cout << "Calculating. Please, wait...\n";
  for (int i=2; i<x; ++i) if (x%i==0) return false;
  return true;
}

int main () {
  // call is_prime(313222313) asynchronously:
  std::future<bool> fut = std::async (is_prime,313222313);

  std::cout << "Checking whether 313222313 is prime.\n";
  // ...

  bool ret = fut.get();      // waits for is_prime to return

  if (ret) std::cout << "It is prime!\n";
  else std::cout << "It is not prime.\n";

  return 0;
}
Checking whether 313222313 is prime.
Calculating. Please, wait...
It is prime!

future

用於訪問一個 provider 對象中的值。

構造

default (1) future() noexcept;
copy [deleted] (2) future (const future&) = delete;
move (3) future (future&& x) noexcept;

賦值

move (1) future& operator= (future&& rhs) noexcept;
copy [deleted] (2) future& operator= (const future&) = delete;

獲取值

檢查 future 是否有效

bool valid() const noexcept;

如果 future 和 shared state 關聯,則返回 true 。默認構造的 future 沒有關聯的 shared state 。

get

generic template (1) T get();
reference specialization (2) R& future<R&>::get(); // when T is a reference type (R&)
void specialization (3) void future<void>::get(); // when T is void

當 shared state 就緒時,獲取 shared state 中的值/異常。

如果 shared state 還未就緒,則阻塞。

等待 shared state 就緒

void wait() const;

如果 shared state 還未就緒,則阻塞。

template <class Rep, class Period>
future_status wait_for (const chrono::duration<Rep,Period>& rel_time) const;

和 wait 類似,但是最多等待 rel_time 時長。

如果 shared state 包含一個 deferred 函數,則立即返回,不阻塞。

返回值如下:

value description
future_status::ready The shared state is ready: the producer has set a value or exception.
future_status::timeout The function waited for rel_time without the shared state becoming ready.
future_status::deferred The shared state contains a deferred function.
template <class Clock, class Duration>
future_status wait_until (const chrono::time_point<Clock,Duration>& abs_time) const;

和 wait_for 類似,只是以絕對時間指定超時時間。

// future::wait
#include <iostream>
#include <future>
#include <chrono>

// a non-optimized way of checking for prime numbers:
bool is_prime (int x) {
  for (int i=2; i<x; ++i) if (x%i==0) return false;
  return true;
}

int main () {
  // call function asynchronously:
  std::future<bool> fut = std::async (is_prime,194232491); 

  std::cout << "checking...\n";
  fut.wait();

  std::cout << "\n194232491 ";
  if (fut.get())      // guaranteed to be ready (and not block) after wait returns
    std::cout << "is prime.\n";
  else
    std::cout << "is not prime.\n";

  return 0;
}
checking...
194232491 is prime.
// future::wait_for
#include <iostream>
#include <future>
#include <chrono>

// a non-optimized way of checking for prime numbers:
bool is_prime (int x) {
  for (int i=2; i<x; ++i) if (x%i==0) return false;
  return true;
}

int main () {
  // call function asynchronously:
  std::future<bool> fut = std::async (is_prime,700020007); 

  // do something while waiting for function to set future:
  std::cout << "checking, please wait";
  std::chrono::milliseconds span (100);
  while (fut.wait_for(span)==std::future_status::timeout)
    std::cout << '.';

  bool x = fut.get();

  std::cout << "\n700020007 " << (x?"is":"is not") << " prime.\n";

  return 0;
}

其他操作

shared_future<T> share();

返回一個 shared_future,該返回值會獲取當前 future 的 shared state 。即,此函數過後,future 沒有與 shared state 關聯。

shared_future

和 future 類似,只是它允許拷貝,允許多個 shared_future 共享一個 shared state ,允許多次訪問 shared state 中的值。

shared state 會一直保持有效,直到最後一個與之關聯的對象釋放該關聯或該對象被銷燬。

構造

default (1) shared_future() noexcept;
copy (2) shared_future (const shared_future& x);
move (3) shared_future (shared_future&& x) noexcept;
move from future (4) shared_future (future<T>&& x) noexcept;

賦值

move (1) shared_future& operator= (shared_future&& rhs) noexcept;
copy (2) shared_future& operator= (const shared_future& rhs);

獲取值

檢查 future 是否有效

bool valid() const noexcept;

如果 shared_future 和 shared state 關聯,則返回 true 。默認構造的 shared_future 沒有關聯的 shared state 。

get

generic template (1) T get();
reference specialization (2) R& shared_future<R&>::get(); // when T is a reference type (R&)
void specialization (3) void shared_future<void>::get(); // when T is void

當 shared state 就緒時,獲取 shared state 中的值/異常。

如果 shared state 還未就緒,則阻塞。

等待 shared state 就緒

void wait() const;

如果 shared state 還未就緒,則阻塞。

template <class Rep, class Period>
future_status wait_for (const chrono::duration<Rep,Period>& rel_time) const;

和 wait 類似,但是最多等待 rel_time 時長。

如果 shared state 包含一個 deferred 函數,則立即返回,不阻塞。

返回值如下:

value description
future_status::ready The shared state is ready: the producer has set a value or exception.
future_status::timeout The function waited for rel_time without the shared state becoming ready.
future_status::deferred The shared state contains a deferred function.
template <class Clock, class Duration>
future_status wait_until (const chrono::time_point<Clock,Duration>& abs_time) const;

和 wait_for 類似,只是以絕對時間指定超時時間。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章