平行容器和物件
平行模式連結庫 (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
不提供erase
或pop_back
方法。 和 一樣vector
,請使用 clear 方法,從concurrent_vector
物件中移除所有專案。類別
concurrent_vector
不會將其元素連續儲存在記憶體中。 因此,您無法concurrent_vector
使用 類別,以使用數位的所有方式。 例如,對於類型concurrent_vector
為的v
變數,表達式&v[0]+2
會產生未定義的行為。類別
concurrent_vector
會 定義grow_by 和 grow_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_back
end
可能會有競爭條件。 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
不提供front
或pop
方法。 類別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_erase
、unsafe_bucket
、unsafe_bucket_count
、 和unsafe_bucket_size
。bucket_size
bucket_count
bucket
erase
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::insert 方法會傳回反覆運算器,而不是
std::pair<iterator, bool>
。類別
concurrent_unordered_multimap
不提供operator[]
或at
方法。
下列範例顯示使用 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::insert 方法會傳回反覆運算器,而不是
std::pair<iterator, bool>
。類別
concurrent_unordered_multiset
不提供operator[]
或at
方法。
下列範例顯示使用 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
物件中。 您也可以多次呼叫 combine
和 combine_each
方法。 如果對象中 combinable
沒有任何本機值變更, combine
則 和 combine_each
方法會在每次呼叫時產生相同的結果。
範例
如需如何使用 類別的 combinable
範例,請參閱下列主題:
[靠上]
[相關主題]
如何:使用平行容器提高效率
示範如何使用平行容器,以平行方式儲存和存取數據。
如何:使用 combinable 改善效能
示範如何使用 combinable
類別來消除共享狀態,進而改善效能。
如何:使用 combinable 結合集合
示範如何使用 函 combine
式來合併線程區域數據集。
平行模式程式庫 (PPL)
描述 PPL,其提供命令式程式設計模型,可促進可擴縮性和方便使用,以開發並行應用程式。
參考
concurrent_unordered_multimap 類別