天天看點

C++11多線程future的使用

#include <iostream>
#include <string>
#include <list>
#include <thread>
#include <future>
#include <cassert>

// 1.promise 對象可以儲存某一類型T的值,該值可被future對象讀取(可能在另外一個線程中),是以 promise
// 也提供了一種線程同步的手段。在 promise 對象構造時可以和一個共享狀态(通常是std::future)相關聯。

static void PrintValue(std::future<int>& value) {
  std::cout << "Wait..." << std::endl;
  // 線程會阻塞在這裡,等待promise去set_value,以此可以達到線程同步,以及線程通信。
  int result = value.get();
  std::cout << "Value: " << result << std::endl;
}

static void Test1() {
  std::promise<int> promise;
  std::future<int> value = promise.get_future();

  std::thread thread(PrintValue, std::ref(value));

  for (int i = 0; i < 5; ++i) {
    std::this_thread::sleep_for(std::chrono::seconds(1));
    std::cout << i + 1 << "s" << std::endl;
  }

  promise.set_value(101);

  thread.join();
}

//--------------------------------------------------------------------------------
// 2.std::packaged_task包裝一個可調用的對象,并且允許異步擷取該可調用對象産生的結果。
// std::packaged_task将其包裝的可調用對象的執行結果傳遞給一個std::future對象,
// 該對象通常在另外一個線程中擷取 std::packaged_task 任務的執行結果。

// std::packaged_task 對象是異步 Provider,它在某一時刻通過調用被包裝的任務來設定共享狀态的值。
// std::future 對象是一個異步傳回對象,通過它可以獲得共享狀态的值,當然在必要的時候需要等待共享狀态标志變為ready.

static int Subtract(int a, int b) {
  std::cout << "Wait";
  for (int i = 0; i < 5; ++i) {
    std::this_thread::sleep_for(std::chrono::seconds(1));
    std::cout << ".";
  }

  std::cout << std::endl;

  return a - b;
}

static void Test2() {
  std::packaged_task<int(int, int)> task(Subtract);  // Subtract的傳回值作為共享狀态的值。
  std::future<int> ret = task.get_future();  // 獲得與 packaged_task 共享狀态相關聯的 future 對象.

  std::thread thread(std::move(task), 101, 1);

  int value = ret.get();  // 阻塞在這裡,等待線程結束。
  std::cout << "Result: " << value << std::endl;

  thread.join();
}

//--------------------------------------------------------------------------------
// 3.std::future可以用來擷取異步任務的結果,是以可以把它當成一種簡單的線程間同步的手段。
// std::future通常由某個Provider建立,你可以把Provider想象成一個異步任務的提供者,
// Provider在某個線程中設定共享狀态的值,與該共享狀态相關聯的std::future對象調用get(通常在另外一個線程中擷取該值,
// 如果共享狀态的标志不為ready,則調用std::future::get會阻塞目前的調用者,直到Provider設定了共享狀态的值
//(此時共享狀态的标志變為ready),std::future::get傳回異步任務的值或異常(如果發生了異常)。

// 一個有效的std::future 對象通常由以下三種 Provider 建立,并和某個共享狀态相關聯,分别是
// std::async 函數,std::promise::get_future。std::packaged_task::get_future。

// 在一個有效的 future 對象上調用get會阻塞目前的調用者,直到Provider設定了共享狀态的值或異常。

// 4.c++11還提供了異步接口std::async,通過這個異步接口可以很友善的擷取線程函數的執行結果。
// std::async會自動建立一個線程去調用線程函數,它傳回一個std::future,這個future中存儲了線程函數傳回的結果,
// 當我們需要線程函數的結果時,直接從future中擷取。

static bool IsOdd(int x) {
  std::this_thread::sleep_for(std::chrono::seconds(5));
  if (x % 2 == 0) {
    return false;
  }

  return true;
}

static void Test3() {
  int num = 15;
  std::future<bool> future = std::async(IsOdd, num);

  std::cout << "Wait";
  //while (future.wait_for(std::chrono::seconds(1)) == std::future_status::timeout) {
  //  std::cout << ".";
  //}
  //std::cout << std::endl;

  // deferred:異步操作還沒開始。
  // ready:異步操作已經完成。
  // timeout:異步操作逾時。
  std::future_status status;
  do {
    status = future.wait_for(std::chrono::seconds(1));
    if (status == std::future_status::deferred) {
      std::cout << "deffered" << std::endl;
    } else if (status == std::future_status::timeout) {
      std::cout << ".";
    } else {
      std::cout << "ready" << std::endl;
    }
  } while (status != std::future_status::ready);

  bool value = future.get();
  std::cout << num << " is odd" << std::endl;
}

//--------------------------------------------------------------------------------

int main() {
  //Test1();
  //Test2();
  Test3();

  return 0;
}