共用方式為


平行容器和物件

平行模式程式庫 (PPL) 包含數個容器和物件,這些容器和物件提供對其項目的執行緒安全存取。

「並行容器」(Concurrent Container) 提供對最重要作業的並行安全存取。 這些容器的功能與標準範本程式庫 (STL) 所提供的容器類似。 例如, concurrency::concurrent_vector 類別類似於 std::vector 類別,除了, concurrent_vector類別可讓您新增項目以平行方式。 當您的平行程式碼需要同時對相同容器進行讀取和寫入存取時,請使用並行容器。

「並行物件」(Concurrent Object) 是各元件並行共用的物件。 以平行方式計算並行物件的狀態,會產生與以循序方式計算該並行物件的狀態相同的結果。 Concurrency::combinable 類別是一個例子為並行處理的物件型別。 combinable 類別可讓您平行執行計算,然後再將這些計算結合成最終的結果。 並行物件可以做為替代方案,來替代使用同步處理機制 (例如 Mutex) 同步處理對共用變數或資源的存取。

章節

本主題詳述下列平行容器和物件。

並行容器:

  • concurrent_vector 類別

    • concurrent_vector 與 vector 之間的差異

    • 並行安全的作業

    • 例外狀況安全

  • concurrent_queue 類別

    • concurrent_queue 與 queue 之間的差異

    • 並行安全的作業

    • Iterator 支援

  • concurrent_unordered_map 類別

    • 差異之間 concurrent_unordered_map 和 unordered_map

    • 並行安全的作業

  • concurrent_unordered_multimap 類別

  • concurrent_unordered_set 類別

  • concurrent_unordered_multiset 類別

並行物件:

  • combinable 類別

    • 方法和功能

    • 範例

concurrent_vector 類別

Concurrency::concurrent_vector 類別是一種序列容器類別,就像 std::vector 類別,可讓您在隨機存取其項目。 concurrent_vector 類別啟用並行安全的附加和項目存取作業。 附加作業並不會讓現有指標或 Iterator 失效。 Iterator 存取和周遊作業也是並行安全的。

Dd504906.collapse_all(zh-tw,VS.110).gifconcurrent_vector 與 vector 之間的差異

concurrent_vector 類別與 vector 類別類似。 concurrent_vector 物件上附加、項目存取和 Iterator 存取作業的複雜度與 vector 物件是相同的。 下列各點說明 concurrent_vectorvector 的差異:

  • concurrent_vector 物件上的附加、項目存取、Iterator 存取和 Iterator 周遊作業是並行安全的。

  • 您只能將項目加至 concurrent_vector 物件的結尾。 concurrent_vector 類別未提供 insert 方法。

  • 當您在 concurrent_vector 物件中附加項目時,這個物件並不會使用移動語意

  • concurrent_vector 類別未提供 erasepop_back 方法。 如同 vector,請使用 clear 方法移除 concurrent_vector 物件中的所有項目。

  • concurrent_vector 類別不會將自己的項目連續儲存在記憶體中。 因此,您無法完全像使用陣列一樣地使用 concurrent_vector 類別。 例如,針對型別為 concurrent_vector 的變數 v,運算式 &v[0]+2 會產生未定義的行為。

  • concurrent_vector 類別會定義 grow_bygrow_to_at_least 方法。 這些方法與 resize 方法類似,不同之處在於它們是並行安全的。

  • 當您在 concurrent_vector 物件中附加項目或變更物件的大小時,這個物件並不會更動其內項目的位置。 這可讓現有指標和 Iterator 在並行作業期間一直保持有效。

  • 執行階段不會針對 bool 型別定義專門的 concurrent_vector 版本。

Dd504906.collapse_all(zh-tw,VS.110).gif並行安全的作業

所有方法在 concurrent_vector 物件中附加項目或是增加物件大小,或者是存取 concurrent_vector 物件中的項目時,都是並行安全的。 這項規則的例外是 resize 方法。

下表顯示常見並行安全的 concurrent_vector 方法和運算子。

at

end

operator[]

begin

front

push_back

back

grow_by

rbegin

capacity

grow_to_at_least

rend

empty

max_size

size

作業執行階段提供的相容性 STL,比方說, reserve,不是安全的並行存取。 下表顯示常見不是並行安全的方法和運算子。

assign

reserve

clear

resize

operator=

shrink_to_fit

修改現有項目值的作業並不是並行安全的。 若要同步處理對相同資料項目的並行讀取和寫入作業,請使用同步處理物件 (例如 reader_writer_lock 物件)。 如需同步處理物件的詳細資訊,請參閱同步處理資料結構

當您將使用 vector 的現有程式碼轉換為使用 concurrent_vector 時,並行作業可能會變更您應用程式的行為。 例如,下列範例會在 concurrent_vector 物件上並行執行兩個工作。 第一個工作會將其他項目附加至 concurrent_vector 物件。 第二個工作會計算同一個物件中所有項目的總和。

// parallel-vector-sum.cpp
// compile with: /EHsc
#include <ppl.h>
#include <concurrent_vector.h>
#include <iostream>

using namespace concurrency;
using namespace std;

int wmain()
{
   // Create a concurrent_vector object that contains a few
   // initial elements.
   concurrent_vector<int> v;
   v.push_back(2);
   v.push_back(3);
   v.push_back(4);

   // Perform two tasks in parallel.
   // The first task appends additional elements to the concurrent_vector object.
   // The second task computes the sum of all elements in the same object.

   parallel_invoke(
      [&v] { 
         for(int i = 0; i < 10000; ++i)
         {
            v.push_back(i);
         }
      },
      [&v] {
         combinable<int> sums;
         for(auto i = begin(v); i != end(v); ++i) 
         {
            sums.local() += *i;
         }     
         wcout << L"sum = " << sums.combine(plus<int>()) << endl;
      }
   );
}

雖然 end 方法是並行安全的,但是並行呼叫 push_back 方法會變更 end 所傳回的值。 Iterator 會周遊的項目數是不一定的。 因此,每次執行時這個程式都會產生不同的結果。

Dd504906.collapse_all(zh-tw,VS.110).gif例外狀況安全

如果成長或指派作業擲回例外狀況,則 concurrent_vector 物件的狀態會變無效。 除非另有陳述,否則處於無效狀態之 concurrent_vector 物件的行為會是未定義的行為。 不過,解構函式一定會釋出物件所配置的記憶體,即使物件處於無效狀態也是一樣。

vector 項目的資料型別 _Ty 必須符合下列需求。 否則,concurrent_vector 類別的行為會是未定義的行為。

  • 解構函式不得擲回例外狀況。

  • 如果預設或複製建構函式擲回例外狀況,則不得以 virtual 關鍵字宣告解構函式,而且解構函式必須能夠與以零初始化的記憶體正常互動。

Top

concurrent_queue 類別

Concurrency::concurrent_queue 類別,就像是 std::queue 類別,可讓您取得其最上層,並傳回項目。 concurrent_queue 類別啟用並行安全的加入佇列和清除佇列作業。 concurrent_queue 類別也提供不是並行安全的 Iterator 支援。

Dd504906.collapse_all(zh-tw,VS.110).gifconcurrent_queue 與 queue 之間的差異

concurrent_queue 類別與 queue 類別類似。 下列各點說明 concurrent_queuequeue 的差異:

  • concurrent_queue 物件上的加入佇列和清除佇列作業是並行安全的。

  • concurrent_queue 類別則提供不是並行安全的 Iterator 支援。

  • concurrent_queue 類別未提供 frontpop 方法。 concurrent_queue 類別會定義 try_pop 方法,藉以取代這些方法。

  • concurrent_queue 類別未提供 back 方法。 因此,您無法參考佇列的結尾。

  • concurrent_queue 類別提供 unsafe_size 方法,而非 size 方法。 unsafe_size 方法不是並行安全的。

Dd504906.collapse_all(zh-tw,VS.110).gif並行安全的作業

所有在 concurrent_queue 物件中加入佇入或清除佇列的方法都是並行安全的。

下表顯示常見並行安全的 concurrent_queue 方法和運算子。

empty

push

get_allocator

try_pop

雖然 empty 方法是並行安全的,但是並行作業可能會造成佇列在 empty 方法傳回之前就變大或變小。

下表顯示常見不是並行安全的方法和運算子。

clear

unsafe_end

unsafe_begin

unsafe_size

Dd504906.collapse_all(zh-tw,VS.110).gifIterator 支援

concurrent_queue 提供不是並行安全的 Iterator。 建議您只將這些 Iterator 用來進行偵錯。

concurrent_queue Iterator 只會以前進方向周遊項目。 下表顯示每個 Iterator 所支援的運算子。

運算子

描述

operator++

前進至佇列中的下一個項目。 這個運算子使用多載,來提供遞增前和遞增後的語意。

operator*

擷取目前項目的參考。

operator->

擷取目前項目的指標。

Top

concurrent_unordered_map 類別

Concurrency::concurrent_unordered_map 類別是關聯的容器類別,就像 std::unordered_map 類別、 使不同長度序列型別的項目會控制 std::pair < const 機碼、 智 >。 您可以將未按順序的地圖想像成字典,您可以加入索引鍵 / 值組,或依據索引鍵查詢特定值。 當您有多重執行緒或所需同時存取共用的容器、 中、 插入或更新該工作時,這個類別是很有用。

下列範例顯示使用的基本結構concurrent_unordered_map。 本範例插入 ['a',' i'] 範圍中的字元按鍵。 因為某種無法判別的運算順序,每個索引鍵的最終值也是某種無法判別的。 不過,則以平行方式執行插入。

// unordered-map-structure.cpp
// compile with: /EHsc
#include <ppl.h>
#include <concurrent_unordered_map.h>
#include <iostream>

using namespace concurrency;
using namespace std;

int wmain() 
{
    //
    // Insert a number of items into the map in parallel.

    concurrent_unordered_map<char, int> map; 

    parallel_for(0, 1000, [&map](int i) {
        char key = 'a' + (i%9); // Geneate a key in the range [a,i].
        int value = i;          // Set the value to i.
        map.insert(make_pair(key, value));
    });

    // Print the elements in the map.
    for_each(begin(map), end(map), [](const pair<char, int>& pr) {
        wcout << L"[" << pr.first << L", " << pr.second << L"] ";
    });
}
/* Sample output:
    [e, 751] [i, 755] [a, 756] [c, 758] [g, 753] [f, 752] [b, 757] [d, 750] [h, 754]
*/

如需範例來使用concurrent_unordered_map執行地圖及減少平行地操作,請參閱HOW TO:平行執行對應和縮減作業

Dd504906.collapse_all(zh-tw,VS.110).gif差異之間 concurrent_unordered_map 和 unordered_map

concurrent_unordered_map 類別與 unordered_map 類別類似。 下列各點說明 concurrent_unordered_mapunordered_map 的差異:

  • The erase, bucket, bucket_count, and bucket_size methods are named unsafe_erase, unsafe_bucket, unsafe_bucket_count, and unsafe_bucket_size, respectively. **unsafe_**命名慣例表示這些方法都不並行存取之。 如需有關並行存取安全的詳細資訊,請參閱並行存取之作業。

  • 插入動作顯示現有的指標或 iterator,不使失效,也不要變更已存在於對應的項目順序。 插入並瀏覽樹狀作業可以同時發生。

  • concurrent_unordered_map支援轉寄僅反覆項目。

  • 插入不會使其失效或更新所傳回的 iterator equal_range。 插入可以將範圍的結尾附加不相等的項目。 開始 iterator 指向相同的項目。

為避免發生死結,方法沒有concurrent_unordered_map持有鎖定時記憶體配置器、 雜湊函式或其他使用者定義的程式碼,它會呼叫。 此外,您必須確定雜湊函式一律會評估為相同的值相等的機碼。 最佳的雜湊函式統一散發金鑰雜湊程式碼空間上。

Dd504906.collapse_all(zh-tw,VS.110).gif並行安全的作業

concurrent_unordered_map類別可讓並行存取之插入] 和 [項目存取作業。 現有的指標或 iterator,並無法確認 insert 的動作。 Iterator 存取和周遊作業也是並行安全的。 下表顯示常用concurrent_unordered_map方法和並行存取之運算子。

at

count

find

key_eq

begin

empty

get_allocator

max_size

cbegin

end

hash_function

operator[]

cend

equal_range

插入

size

雖然count可以安全地呼叫方法,從幫助同時執行執行緒、 不同的執行緒可以得到不同的結果加入至容器同時插入新的值。

下表顯示常用的方法並不是安全的並行處理的操作員。

clear

max_load_factor

rehash

load_factor

operator=

交換

除了這些方法的任何方法的開頭是**unsafe_**也不是並行存取之。

Top

concurrent_unordered_multimap 類別

Concurrency::concurrent_unordered_multimap 類別非常類似concurrent_unordered_map類別的不同之處在於它可以讓多個值將對應到相同的金鑰。 它也會不同於concurrent_unordered_map以下列方式:

下列範例顯示使用的基本結構concurrent_unordered_multimap。 本範例插入 ['a',' i'] 範圍中的字元按鍵。 concurrent_unordered_multimap可以有多個值的索引鍵。

// unordered-multimap-structure.cpp
// compile with: /EHsc
#include <ppl.h>
#include <concurrent_unordered_map.h>
#include <iostream>

using namespace concurrency;
using namespace std;

int wmain() 
{
    //
    // Insert a number of items into the map in parallel.

    concurrent_unordered_multimap<char, int> map; 

    parallel_for(0, 10, [&map](int i) {
        char key = 'a' + (i%9); // Geneate a key in the range [a,i].
        int value = i;          // Set the value to i.
        map.insert(make_pair(key, value));
    });

    // Print the elements in the map.
    for_each(begin(map), end(map), [](const pair<char, int>& pr) {
        wcout << L"[" << pr.first << L", " << pr.second << L"] ";
    });
}
/* Sample output:
    [e, 4] [i, 8] [a, 9] [a, 0] [c, 2] [g, 6] [f, 5] [b, 1] [d, 3] [h, 7]
*/

Top

concurrent_unordered_set 類別

Concurrency::concurrent_unordered_set 類別非常類似concurrent_unordered_map類別的不同之處在於它管理的而不是索引鍵和值組的值。 concurrent_unordered_set類別未提供operator[]at方法。

下列範例顯示使用的基本結構concurrent_unordered_set。 本範例插入 ['a',' i'] 範圍中的字元值。 則以平行方式執行插入。

// unordered-set-structure.cpp
// compile with: /EHsc
#include <ppl.h>
#include <concurrent_unordered_set.h>
#include <iostream>

using namespace concurrency;
using namespace std;

int wmain() 
{
    //
    // Insert a number of items into the set in parallel.

    concurrent_unordered_set<char> set; 

    parallel_for(0, 10000, [&set](int i) {
        set.insert('a' + (i%9)); // Geneate a value in the range [a,i].
    });

    // Print the elements in the set.
    for_each(begin(set), end(set), [](char c) {
        wcout << L"[" << c << L"] ";
    });
}
/* Sample output:
    [e] [i] [a] [c] [g] [f] [b] [d] [h]
*/

Top

concurrent_unordered_multiset 類別

Concurrency::concurrent_unordered_multiset 類別非常類似concurrent_unordered_set類別的不同之處在於它允許重複的值。 它也會不同於concurrent_unordered_set以下列方式:

下列範例顯示使用的基本結構concurrent_unordered_multiset。 本範例插入 ['a',' i'] 範圍中的字元值。 concurrent_unordered_multiset可以讓發生多次的值。

// unordered-set-structure.cpp
// compile with: /EHsc
#include <ppl.h>
#include <concurrent_unordered_set.h>
#include <iostream>

using namespace concurrency;
using namespace std;

int wmain() 
{
    //
    // Insert a number of items into the set in parallel.

    concurrent_unordered_multiset<char> set; 

    parallel_for(0, 40, [&set](int i) {
        set.insert('a' + (i%9)); // Geneate a value in the range [a,i].
    });

    // Print the elements in the set.
    for_each(begin(set), end(set), [](char c) {
        wcout << L"[" << c << L"] ";
    });
}
/* Sample output:
    [e] [e] [e] [e] [i] [i] [i] [i] [a] [a] [a] [a] [a] [c] [c] [c] [c] [c] [g] [g]
    [g] [g] [f] [f] [f] [f] [b] [b] [b] [b] [b] [d] [d] [d] [d] [d] [h] [h] [h] [h]
*/

Top

combinable 類別

Concurrency::combinable 類別會提供可重複使用、 執行緒區域儲存區,可讓您執行更細緻的運算,然後將這些計算合併到最終的結果。 您可以將 combinable 物件視為削減變數。

當您的資源會供數個執行緒或工作共用時,combinable 類別會很有用。 combinable 類別可藉由以無法鎖定的方式提供對共用資源的存取,協助您消除共用狀態。 因此,這個類別提供另一種替代方式,來替代使用同步處理機制 (例如 Mutex) 同步處理多個執行緒對共用資料的存取。

Dd504906.collapse_all(zh-tw,VS.110).gif方法和功能

下表顯示 combinable 類別的一些重要方法。 如需所有 combinable 類別方法的詳細資訊,請參閱 combinable 類別

方法

描述

local

擷取與目前執行緒內容相關聯之區域變數的參考。

clear

移除 combinable 物件中的所有執行緒區域變數。

combine

combine_each

使用提供的 combine 函式,從一組全部都是在執行緒區域執行的計算來產生最終的值。

combinable 類別是在最終合併結果上參數化的範本類別。 如果您呼叫預設建構函式,則 _Ty 範本參數型別必須要有預設建構函式和複製建構函式。 如果 _Ty 範本參數型別沒有預設建構函式,請呼叫以初始設定函式為參數的建構函式多載版本。

呼叫 combinecombine_each 方法之後,您就可以在 combinable 物件中儲存額外的資料。 您也可以多次呼叫 combinecombine_each 方法。 如果 combinable 物件中的區域數值未變更,則每次呼叫 combinecombine_each 方法都會產生相同的結果。

Dd504906.collapse_all(zh-tw,VS.110).gif範例

如需如何使用 combinable 類別的範例,請參閱下列主題:

Top

相關主題

參考資料

concurrent_vector 類別

concurrent_queue 類別

concurrent_unordered_map 類別

concurrent_unordered_multimap 類別

concurrent_unordered_set 類別

concurrent_unordered_multiset 類別

combinable 類別