共用方式為


平行容器和物件

平行模式連結庫 (PPL) 包含數個容器和物件,可提供安全線程存取其元素。

並行 容器 可為最重要的作業提供並行安全存取。 在這裡,並行安全表示指標或反覆運算器一律有效。 這不是元素初始化或特定周遊順序的保證。 這些容器的功能類似於 C++ 標準連結庫所提供的容器。 例如, 並行::concurrent_vector 類別類似於 std::vector 類別,不同之處在於類別 concurrent_vector 可讓您平行附加元素。 當您有需要相同容器讀取和寫入存取權的平行程序代碼時,請使用並行容器。

並行物件會在元件之間同時共用。 以平行方式計算並行物件狀態的進程,會產生與另一個以序列方式計算相同狀態的進程相同的結果。 並行::combinable 類別是並行物件類型的其中一個範例。 類別 combinable 可讓您平行執行計算,然後將這些計算合併為最終結果。 當您使用同步處理機制,例如 Mutex 來同步存取共用變數或資源時,請使用並行物件。

區段

本主題詳細說明下列平行容器和物件。

並行容器:

並行物件:

concurrent_vector 類別

concurrency::concurrent_vector 類別是序列容器類別,就像 std::vector 類別一樣,可讓您隨機存取其元素。 類別 concurrent_vector 會啟用並行安全附加和元素存取作業。 附加作業不會使現有的指標或反覆運算器失效。 反覆運算器存取和周遊作業也是並行安全。 在這裡,並行安全表示指標或反覆運算器一律有效。 這不是元素初始化或特定周遊順序的保證。

concurrent_vector與向量之間的差異

類別與類別 concurrent_vector 非常類似 vector 。 物件上的 concurrent_vector 附加、專案存取和反覆運算器存取作業的複雜度與 vector 物件相同。 下列幾點說明與 的不同vector之處concurrent_vector

  • 附加、元素存取、反覆運算器存取,以及物件上的 concurrent_vector 反覆運算器周遊作業都是並行安全。

  • 您只能將元素新增至 物件的結尾 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當您附加至對象或調整其大小時,物件不會重新放置其元素。 這可讓現有的指標和反覆運算器在並行作業期間保持有效。

  • 運行時間不會針對 型別bool定義 的特製化版本concurrent_vector

並行安全作業

附加至或增加物件大小 concurrent_vector 或存取 物件中 concurrent_vector 專案的所有方法都是並行安全的方法。 在這裡,並行安全表示指標或反覆運算器一律有效。 這不是元素初始化或特定周遊順序的保證。 這個規則的例外狀況是 resize 方法。

下表顯示並行安全通用 concurrent_vector 方法和運算元。

運行時間為與 C++ 標準連結庫相容而提供的作業,例如, reserve不是並行安全。 下表顯示非並行安全通用方法和運算符。

修改現有專案值的作業不是並行安全作業。 使用同步處理物件,例如 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傳回的值變更。 反覆運算器周游的項目數目不確定。 因此,每次執行此程式時,都可能會產生不同的結果。 當專案類型不簡單時,和呼叫之間push_backend可能會有競爭條件。 end方法可能會傳回已配置但未完全初始化的專案。

例外狀況安全性

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

向量項目的 T數據類型必須符合下列需求。 否則,類別的行為 concurrent_vector 是未定義的。

  • 解構函式不得擲回。

  • 如果預設或複製建構函式擲回,則解構函式不得使用 virtual 關鍵詞來宣告,而且必須使用零初始化的記憶體正確運作。

[靠上]

concurrent_queue 類別

concurrency::concurrent_queue 類別,就像 std::queue 類別一樣,可讓您存取其前後元素。 類別 concurrent_queue 會啟用並行安全加入佇列和清除佇列作業。 在這裡,並行安全表示指標或反覆運算器一律有效。 這不是元素初始化或特定周遊順序的保證。 類別 concurrent_queue 也提供非並行安全之反覆運算器支援。

concurrent_queue和佇列之間的差異

類別與類別 concurrent_queue 非常類似 queue 。 下列幾點說明與 的不同queue之處concurrent_queue

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

  • 類別 concurrent_queue 提供非並行安全之反覆運算器支援。

  • 類別 concurrent_queue 不提供 frontpop 方法。 類別 concurrent_queue 會藉由定義 try_pop 方法來取代這些方法。

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

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

並行安全作業

加入佇列或從物件取消佇列 concurrent_queue 的所有方法都是並行安全的方法。 在這裡,並行安全表示指標或反覆運算器一律有效。 這不是元素初始化或特定周遊順序的保證。

下表顯示並行安全通用 concurrent_queue 方法和運算元。

雖然方法 empty 是並行安全的方法,但並行作業可能會導致佇列在方法傳回之前 empty 成長或縮小。

下表顯示非並行安全通用方法和運算符。

Iterator 支援

concurrent_queue提供非並行安全的反覆運算器。 建議您只使用這些反覆運算器進行偵錯。

concurrent_queue反覆運算器只會周遊正向方向的專案。 下表顯示每個反覆運算器支援的運算元。

Operator 描述
operator++ 前進到佇列中的下一個專案。 此運算符會多載以提供前置遞增和後遞增語意。
operator* 擷取目前項目的參考。
operator-> 擷取目前專案的指標。

[靠上]

concurrent_unordered_map 類別

concurrency::concurrent_unordered_map 類別是關聯容器類別,就像 std::unordered_map 類別一樣,控制 std::p air<const Key, Ty> 類型的不同長度元素序列。 將未排序的對應視為字典,您可以將索引鍵和值組新增至或依索引鍵查閱值。 當您有多個線程或工作必須同時存取共用容器、將其插入或更新時,這個類別很有用。

下列範例顯示使用 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 以平行方式執行對應和縮減作業的範例,請參閱 如何:以平行方式執行對應和縮減作業。

concurrent_unordered_map與unordered_map之間的差異

類別與類別 concurrent_unordered_map 非常類似 unordered_map 。 下列幾點說明與 的不同unordered_map之處concurrent_unordered_map

  • 、、 和 方法分別命名為 unsafe_eraseunsafe_bucketunsafe_bucket_count、 和 unsafe_bucket_sizebucket_size bucket_countbucketerase unsafe_命名慣例表示這些方法不是並行安全的方法。 如需並行安全性的詳細資訊,請參閱 並行安全作業

  • 插入作業不會使現有的指標或反覆運算器失效,也不會變更對應中已經存在的項目順序。 插入和周遊作業可以同時進行。

  • concurrent_unordered_map 僅支援向前反覆運算。

  • 插入不會使 所傳 equal_range回的反覆運算器失效或更新。 插入可以將不相等的專案附加至範圍的結尾。 開始反覆運算器指向相等專案。

為了協助避免死結,在呼叫記憶體配置器、哈希函式或其他使用者定義程式代碼時,不會保留鎖定的方法 concurrent_unordered_map 。 此外,您必須確定哈希函式一律會評估相同值的相等索引鍵。 最佳哈希函式會將索引鍵統一分散到哈希碼空間。

並行安全作業

類別 concurrent_unordered_map 會啟用並行安全插入和元素存取作業。 插入作業不會使現有的指標或反覆運算器失效。 反覆運算器存取和周遊作業也是並行安全。 在這裡,並行安全表示指標或反覆運算器一律有效。 這不是元素初始化或特定周遊順序的保證。 下表顯示並行 concurrent_unordered_map 安全常用的方法和運算符。

count雖然可以從並行執行的線程安全地呼叫 方法,但如果同時將新的值插入容器中,不同的線程可能會接收不同的結果。

下表顯示非並行安全常用的方法和運算符。

除了這些方法之外,以 開頭 unsafe_ 的任何方法也不是並行安全的方法。

[靠上]

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]
*/

[靠上]

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]
*/

[靠上]

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]
*/

[靠上]

combinable 類別

concurrency::combinable 類別提供可重複使用的線程本機記憶體,可讓您執行精細的計算,然後將這些計算合併至最終結果。 您可以將 combinable 物件視為削減變數。

當您有數個線程或工作之間共用的資源時,類別 combinable 會很有用。 類別 combinable 可協助您排除共享狀態,方法是以無鎖定方式提供共用資源的存取權。 因此,這個類別提供使用同步處理機制的替代方法,例如 Mutex,以同步存取多個線程的共享數據。

方法和功能

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

方法 描述
local 擷取與目前線程內容相關聯的局部變數參考。
clear 從物件中移除所有線程局部變數 combinable
combine

combine_each
使用提供的 combine 函式,從所有線程區域計算集產生最終值。

類別 combinable 是範本類別,會在最終合併的結果上參數化。 如果您呼叫預設建構函式, T 範本參數類型必須具有預設建構函式和複製建構函式。 T如果範本參數類型沒有預設建構函式,請呼叫採用初始化函式做為其參數的建構函式多載版本。

呼叫 combine 或 combine_each 方法之後,您可以將其他數據儲存在 combinable 物件中。 您也可以多次呼叫 combinecombine_each 方法。 如果對象中 combinable 沒有任何本機值變更, combine 則 和 combine_each 方法會在每次呼叫時產生相同的結果。

範例

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

[靠上]

如何:使用平行容器提高效率
示範如何使用平行容器,以平行方式儲存和存取數據。

如何:使用 combinable 改善效能
示範如何使用 combinable 類別來消除共享狀態,進而改善效能。

如何:使用 combinable 結合集合
示範如何使用 函 combine 式來合併線程區域數據集。

平行模式程式庫 (PPL)
描述 PPL,其提供命令式程式設計模型,可促進可擴縮性和方便使用,以開發並行應用程式。

參考

concurrent_vector 類別

concurrent_queue 類別

concurrent_unordered_map 類別

concurrent_unordered_multimap 類別

concurrent_unordered_set 類別

concurrent_unordered_multiset 類別

combinable 類別