Share via


逐步解說:實作未來

本主題將示範如何在您的應用程式中實作未來。 本主題會示範如何將並行執行階段中的現有功能合併成可執行更多作業的項目。

重要

為了便於示範本主題說明未來的概念。當您需要計算值供日後使用的非同步工作時,建議您使用 std::futureconcurrency::task

「工作」(Task) 是一項可以分解為更多細部計算的計算。 「未來」(Future) 是計算值供日後使用的非同步工作。

為了實作未來,本主題會定義 async_future 類別。 async_future 類別會使用並行執行階段的下列元件:concurrency::task_group 類別和 concurrency::single_assignment 類別。 async_future 類別會使用 task_group 類別,以非同步方式計算某個值,並且使用 single_assignment 類別來儲存計算的結果。 async_future 類別的建構函式會接受計算結果的工作函式,而且 get 方法會擷取結果。

若要實作 async_future 類別

  1. 宣告名為 async_future 而且根據結果計算型別參數化的樣板類別。 將 public 和 private 區段加入至這個類別。

    template <typename T>
    class async_future
    {
    public:
    private:
    };
    
  2. async_future 類別的 private 區段中,宣告 task_groupsingle_assignment 資料成員。

    // Executes the asynchronous work function.
    task_group _tasks;
    
    // Stores the result of the asynchronous work function.
    single_assignment<T> _value;
    
  3. async_future 類別的 public 區段中,實作建構函式。 此建構函式就是根據計算結果之工作函式參數化的範本。 此建構函式會以非同步方式執行 task_group 資料成員中的工作函式,並且使用 concurrency::send 函式,將結果寫入 single_assignment 資料成員。

    template <class Functor>
    explicit async_future(Functor&& fn)
    {
       // Execute the work function in a task group and send the result 
       // to the single_assignment object.
       _tasks.run([fn, this]() {
          send(_value, fn());
        });
    }
    
  4. async_future 類別的 public 區段中,實作解構函式。 此解構函式會等候工作完成。

    ~async_future()
    {
       // Wait for the task to finish.
       _tasks.wait();
    }
    
  5. async_future 類別的 public 區段中,實作 get 方法。 這個方法會使用 concurrency::receive 函式來擷取工作函式的結果。

    // Retrieves the result of the work function. 
    // This method blocks if the async_future object is still  
    // computing the value.
    T get()
    { 
       return receive(_value); 
    }
    

範例

說明

下列範例說明完整的 async_future 類別及其使用方式的範例。 wmain 函式會建立包含 10,000 個隨機整數值的 std::vector 物件。 然後,它會使用 async_future 物件來尋找 vector 物件中所包含的最小值和最大值。

程式碼

// futures.cpp 
// compile with: /EHsc
#include <ppl.h>
#include <agents.h>
#include <vector>
#include <algorithm>
#include <iostream>
#include <numeric>
#include <random>

using namespace concurrency;
using namespace std;

template <typename T>
class async_future
{
public:
   template <class Functor>
   explicit async_future(Functor&& fn)
   {
      // Execute the work function in a task group and send the result 
      // to the single_assignment object.
      _tasks.run([fn, this]() {
         send(_value, fn());
       });
   }

   ~async_future()
   {
      // Wait for the task to finish.
      _tasks.wait();
   }

   // Retrieves the result of the work function. 
   // This method blocks if the async_future object is still  
   // computing the value.
   T get()
   { 
      return receive(_value); 
   }

private:
   // Executes the asynchronous work function.
   task_group _tasks;

   // Stores the result of the asynchronous work function.
   single_assignment<T> _value;
};

int wmain()
{
   // Create a vector of 10000 integers, where each element  
   // is between 0 and 9999.
   mt19937 gen(2);
   vector<int> values(10000);   
   generate(begin(values), end(values), [&gen]{ return gen()%10000; });

   // Create a async_future object that finds the smallest value in the 
   // vector.
   async_future<int> min_value([&]() -> int { 
      int smallest = INT_MAX;
      for_each(begin(values), end(values), [&](int value) {
         if (value < smallest)
         {
            smallest = value;
         }
      });
      return smallest;
   });

   // Create a async_future object that finds the largest value in the 
   // vector.
   async_future<int> max_value([&]() -> int { 
      int largest = INT_MIN;
      for_each(begin(values), end(values), [&](int value) {
         if (value > largest)
         {
            largest = value;
         } 
      });
      return largest;
   });

   // Calculate the average value of the vector while the async_future objects 
   // work in the background. 
   int sum = accumulate(begin(values), end(values), 0);
   int average = sum / values.size();

   // Print the smallest, largest, and average values.
   wcout << L"smallest: " << min_value.get() << endl
         << L"largest:  " << max_value.get() << endl
         << L"average:  " << average << endl;
}

註解

這個範例會產生下列輸出:

  

此範例會使用 async_future::get 方法來擷取計算的結果。 如果計算仍然在進行中,async_future::get 方法會等候計算完成。

健全的程式設計

若要擴充 async_future 類別,以便處理工作函式所擲回的例外狀況,請將 async_future::get 方法修改成呼叫 concurrency::task_group::wait 方法。 task_group::wait 方法會擲回工作函式所產生的任何例外狀況。

下列範例顯示 async_future 類別的已修改版本。 wmain 函式會使用 try-catch 區塊來列印 async_future 物件的結果,或列印工作函式所產生之例外狀況的值。

// futures-with-eh.cpp 
// compile with: /EHsc
#include <ppl.h>
#include <agents.h>
#include <vector>
#include <algorithm>
#include <iostream>

using namespace concurrency;
using namespace std;

template <typename T>
class async_future
{
public:
   template <class Functor>
   explicit async_future(Functor&& fn)
   {
      // Execute the work function in a task group and send the result 
      // to the single_assignment object.
      _tasks.run([fn, this]() {
         send(_value, fn());
       });
   }

   ~async_future()
   {
      // Wait for the task to finish.
      _tasks.wait();
   }

   // Retrieves the result of the work function. 
   // This method blocks if the async_future object is still 
   // computing the value.
   T get()
   { 
      // Wait for the task to finish. 
      // The wait method throws any exceptions that were generated 
      // by the work function.
      _tasks.wait();

      // Return the result of the computation. 
      return receive(_value);
   }

private:
   // Executes the asynchronous work function.
   task_group _tasks;

   // Stores the result of the asynchronous work function.
   single_assignment<T> _value;
};

int wmain()
{
   // For illustration, create a async_future with a work  
   // function that throws an exception.
   async_future<int> f([]() -> int { 
      throw exception("error");
   });

   // Try to read from the async_future object.  
   try
   {
      int value = f.get();
      wcout << L"f contains value: " << value << endl;
   }
   catch (const exception& e)
   {
      wcout << L"caught exception: " << e.what() << endl;
   }
}

這個範例會產生下列輸出:

  

如需並行執行階段中例外狀況處理模型的詳細資訊,請參閱並行執行階段的例外狀況處理

編譯程式碼

請複製範例程式碼,並將它貼在 Visual Studio 專案中,或貼在名為 futures.cpp 的檔案中,然後在 Visual Studio 的 [命令提示字元] 視窗中執行下列命令。

cl.exe /EHsc futures.cpp

請參閱

參考

task_group 類別

single_assignment 類別

概念

並行執行階段的例外狀況處理

其他資源

並行執行階段逐步解說