Kontainer dan Objek Paralel
Pustaka Pola Paralel (PPL) mencakup beberapa kontainer dan objek yang menyediakan akses aman utas ke elemennya.
Kontainer bersamaan menyediakan akses aman konkurensi ke operasi yang paling penting. Di sini, konkurensi-aman berarti pointer atau iterator selalu valid. Ini bukan jaminan inisialisasi elemen, atau urutan traversal tertentu. Fungsionalitas kontainer ini menyerupan kontainer yang disediakan oleh Pustaka Standar C++. Misalnya, kelas konkurensi::concurrent_vector menyerupai kelas std::vector , kecuali bahwa concurrent_vector
kelas memungkinkan Anda menambahkan elemen secara paralel. Gunakan kontainer bersamaan saat Anda memiliki kode paralel yang memerlukan akses baca dan tulis ke kontainer yang sama.
Objek bersamaan dibagikan secara bersamaan di antara komponen. Proses yang menghitung status objek bersamaan secara paralel menghasilkan hasil yang sama dengan proses lain yang menghitung status yang sama secara serial. Kelas konkurensi::combinable adalah salah satu contoh jenis objek bersamaan. Kelas ini combinable
memungkinkan Anda melakukan komputasi secara paralel, lalu menggabungkan komputasi tersebut menjadi hasil akhir. Gunakan objek bersamaan saat Anda akan menggunakan mekanisme sinkronisasi, misalnya, mutex, untuk menyinkronkan akses ke variabel atau sumber daya bersama.
Bagian
Topik ini menjelaskan kontainer dan objek paralel berikut secara rinci.
Kontainer bersamaan:
Objek bersamaan:
Kelas concurrent_vector
Kelas konkurensi::concurrent_vector adalah kelas kontainer urutan yang, sama seperti kelas std::vector , memungkinkan Anda mengakses elemennya secara acak. Kelas ini concurrent_vector
memungkinkan operasi penambalan dan akses elemen yang aman konkurensi. Operasi penambah tidak membatalkan penunjuk atau iterator yang ada. Akses iterator dan operasi traversal juga aman konkurensi. Di sini, konkurensi-aman berarti pointer atau iterator selalu valid. Ini bukan jaminan inisialisasi elemen, atau urutan traversal tertentu.
Perbedaan Antara concurrent_vector dan vektor
Kelas concurrent_vector
ini sangat menyerupai vector
kelas. Kompleksitas menambahkan, akses elemen, dan operasi akses iterator pada concurrent_vector
objek sama dengan untuk vector
objek. Poin-poin berikut menggambarkan di mana concurrent_vector
berbeda dari vector
:
Menambahkan, akses elemen, akses iterator, dan operasi traversal iterator pada
concurrent_vector
objek aman konkurensi.Anda hanya dapat menambahkan elemen ke akhir
concurrent_vector
objek. Kelasconcurrent_vector
tidak menyediakaninsert
metode .Objek
concurrent_vector
tidak menggunakan semantik pemindahan saat Anda menambahkannya.Kelas
concurrent_vector
tidak menyediakanerase
metode ataupop_back
.vector
Seperti halnya , gunakan metode yang jelas untuk menghapus semua elemen dariconcurrent_vector
objek.Kelas
concurrent_vector
tidak menyimpan elemennya secara berdekatan dalam memori. Oleh karena itu, Anda tidak dapat menggunakan kelas denganconcurrent_vector
semua cara agar Anda dapat menggunakan array. Misalnya, untuk variabel bernamav
jenisconcurrent_vector
, ekspresi&v[0]+2
menghasilkan perilaku yang tidak ditentukan.Kelas
concurrent_vector
menentukan metode grow_by dan grow_to_at_least . Metode ini menyerupai metode mengubah ukuran , kecuali bahwa metode tersebut aman konkurensi.Objek
concurrent_vector
tidak merelokasi elemennya saat Anda menambahkan atau mengubah ukurannya. Ini memungkinkan pointer dan iterator yang ada untuk tetap valid selama operasi bersamaan.Runtime tidak menentukan versi khusus untuk jenis
concurrent_vector
bool
.
Operasi Brankas Konkurensi
Semua metode yang menambahkan atau meningkatkan ukuran concurrent_vector
objek, atau mengakses elemen dalam concurrent_vector
objek, aman konkurensi. Di sini, konkurensi-aman berarti pointer atau iterator selalu valid. Ini bukan jaminan inisialisasi elemen, atau urutan traversal tertentu. Pengecualian untuk aturan ini adalah resize
metode .
Tabel berikut menunjukkan metode dan operator umum concurrent_vector
yang aman konkurensi.
Operasi yang disediakan runtime untuk kompatibilitas dengan Pustaka Standar C++, misalnya, reserve
, tidak aman konkurensi. Tabel berikut menunjukkan metode dan operator umum yang tidak aman konkurensi.
Operasi yang memodifikasi nilai elemen yang ada tidak aman konkurensi. Gunakan objek sinkronisasi seperti objek reader_writer_lock untuk menyinkronkan operasi baca dan tulis bersamaan ke elemen data yang sama. Untuk informasi selengkapnya tentang objek sinkronisasi, lihat Struktur Data Sinkronisasi.
Ketika Anda mengonversi kode yang ada yang menggunakan vector
untuk menggunakan concurrent_vector
, operasi bersamaan dapat menyebabkan perilaku aplikasi Anda berubah. Misalnya, pertimbangkan program berikut yang secara bersamaan melakukan dua tugas pada objek concurrent_vector
. Tugas pertama menambahkan elemen tambahan ke concurrent_vector
objek. Tugas kedua menghitung jumlah semua elemen dalam objek yang sama.
// 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
Meskipun metode ini aman konkurensi, panggilan bersamaan ke metode push_back menyebabkan nilai yang dikembalikan oleh end
untuk berubah. Jumlah elemen yang dilalui iterator tidak ditentukan. Oleh karena itu, program ini dapat menghasilkan hasil yang berbeda setiap kali Anda menjalankannya. Ketika jenis elemen tidak sepele, ada kemungkinan kondisi balapan ada di antara push_back
dan end
panggilan. Metode end
ini dapat mengembalikan elemen yang dialokasikan, tetapi tidak sepenuhnya diinisialisasi.
Pengecualian Brankas ty
Jika operasi pertumbuhan atau penugasan melempar pengecualian, status concurrent_vector
objek menjadi tidak valid. Perilaku concurrent_vector
objek yang dalam status tidak valid tidak terdefinisi kecuali dinyatakan lain. Namun, destruktor selalu membebaskan memori yang dialokasikan objek, bahkan jika objek dalam keadaan tidak valid.
Jenis data elemen vektor, T
, harus memenuhi persyaratan berikut. Jika tidak, perilaku concurrent_vector
kelas tidak ditentukan.
Destruktor tidak boleh melempar.
Jika konstruktor default atau salin dilemparkan, destruktor tidak boleh dideklarasikan dengan menggunakan
virtual
kata kunci dan harus bekerja dengan benar dengan memori yang diinisialisasi nol.
[Atas]
Kelas concurrent_queue
Kelas konkurensi::concurrent_queue , sama seperti kelas std::queue , memungkinkan Anda mengakses elemen depan dan belakangnya. Kelas ini concurrent_queue
memungkinkan operasi antrean dan penghapusan antrean yang aman konkurensi. Di sini, konkurensi-aman berarti pointer atau iterator selalu valid. Ini bukan jaminan inisialisasi elemen, atau urutan traversal tertentu. Kelas ini concurrent_queue
juga menyediakan dukungan iterator yang tidak aman konkurensi.
Perbedaan Antara concurrent_queue dan antrean
Kelas concurrent_queue
ini sangat menyerupai queue
kelas. Poin-poin berikut menggambarkan di mana concurrent_queue
berbeda dari queue
:
Operasi antrean dan penghapusan antrean pada
concurrent_queue
objek aman konkurensi.Kelas
concurrent_queue
menyediakan dukungan iterator yang tidak aman konkurensi.Kelas
concurrent_queue
tidak menyediakanfront
metode ataupop
. Kelasconcurrent_queue
menggantikan metode ini dengan mendefinisikan metode try_pop .Kelas
concurrent_queue
tidak menyediakanback
metode . Oleh karena itu, Anda tidak dapat mereferensikan akhir antrean.Kelas
concurrent_queue
menyediakan metode unsafe_size alih-alihsize
metode . Metodeunsafe_size
ini tidak aman konkurensi.
Operasi Brankas Konkurensi
Semua metode yang mengantre ke atau menghapus antrean dari concurrent_queue
objek aman konkurensi. Di sini, konkurensi-aman berarti pointer atau iterator selalu valid. Ini bukan jaminan inisialisasi elemen, atau urutan traversal tertentu.
Tabel berikut menunjukkan metode dan operator umum concurrent_queue
yang aman konkurensi.
empty
Meskipun metode ini aman konkurensi, operasi bersamaan dapat menyebabkan antrean tumbuh atau menyusut sebelum empty
metode kembali.
Tabel berikut menunjukkan metode dan operator umum yang tidak aman konkurensi.
Dukungan Iterator
menyediakan concurrent_queue
iterator yang tidak aman konkurensi. Kami menyarankan agar Anda menggunakan iterator ini hanya untuk penelusuran kesalahan.
Iterator concurrent_queue
hanya melintasi elemen ke arah depan. Tabel berikut ini memperlihatkan operator yang didukung setiap iterator.
Operator | Deskripsi |
---|---|
operator++ |
Maju ke item berikutnya dalam antrean. Operator ini kelebihan beban untuk menyediakan semantik pra-kenaikan dan pasca-kenaikan. |
operator* |
Mengambil referensi ke item saat ini. |
operator-> |
Mengambil penunjuk ke item saat ini. |
[Atas]
Kelas concurrent_unordered_map
Kelas konkurensi::concurrent_unordered_map adalah kelas kontainer asosiatif yang, sama seperti kelas std::unordered_map , mengontrol urutan elemen tipe std::p air<const Key yang bervariasi, Ty>. Anggap peta yang tidak diurutkan sebagai kamus yang dapat Anda tambahkan pasangan kunci dan nilainya atau cari nilai menurut kunci. Kelas ini berguna ketika Anda memiliki beberapa utas atau tugas yang harus mengakses kontainer bersamaan secara bersamaan, menyisipkan ke dalamnya, atau memperbaruinya.
Contoh berikut menunjukkan struktur dasar untuk menggunakan concurrent_unordered_map
. Contoh ini menyisipkan kunci karakter dalam rentang ['a', 'i']. Karena urutan operasi tidak ditentukan, nilai akhir untuk setiap kunci juga tidak ditentukan. Namun, aman untuk melakukan penyisipan secara paralel.
// 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]
*/
Untuk contoh yang menggunakan concurrent_unordered_map
untuk melakukan peta dan mengurangi operasi secara paralel, lihat Cara: Melakukan Operasi Peta dan Kurangi secara Paralel.
Perbedaan antara concurrent_unordered_map dan unordered_map
Kelas concurrent_unordered_map
ini sangat menyerupai unordered_map
kelas. Poin-poin berikut menggambarkan di mana concurrent_unordered_map
berbeda dari unordered_map
:
Metode
erase
,bucket
,bucket_count
, danbucket_size
masing-masing diberi namaunsafe_erase
, ,unsafe_bucket
unsafe_bucket_count
, danunsafe_bucket_size
. Konvensiunsafe_
penamaan menunjukkan bahwa metode ini tidak aman konkurensi. Untuk informasi selengkapnya tentang keamanan konkurensi, lihat Operasi konkurensi-Brankas.Operasi sisipkan tidak membatalkan penunjuk atau iterator yang ada, juga tidak mengubah urutan item yang sudah ada di peta. Operasi sisipkan dan melintasi dapat terjadi secara bersamaan.
concurrent_unordered_map
hanya mendukung perulangan penerusan.Penyisipan tidak membatalkan atau memperbarui iterator yang dikembalikan oleh
equal_range
. Penyisipan dapat menambahkan item yang tidak sama ke akhir rentang. Iterator mulai menunjuk ke item yang sama.
Untuk membantu menghindari kebuntuan concurrent_unordered_map
, tidak ada metode menahan kunci saat memanggil alokator memori, fungsi hash, atau kode lain yang ditentukan pengguna. Selain itu, Anda harus memastikan bahwa fungsi hash selalu mengevaluasi kunci yang sama dengan nilai yang sama. Fungsi hash terbaik mendistribusikan kunci secara seragam di seluruh ruang kode hash.
Operasi Brankas Konkurensi
Kelas ini concurrent_unordered_map
memungkinkan operasi sisipan dan akses elemen yang aman konkurensi. Operasi sisipkan tidak membatalkan penunjuk atau iterator yang ada. Akses iterator dan operasi traversal juga aman konkurensi. Di sini, konkurensi-aman berarti pointer atau iterator selalu valid. Ini bukan jaminan inisialisasi elemen, atau urutan traversal tertentu. Tabel berikut menunjukkan metode dan operator yang umum digunakan concurrent_unordered_map
yang aman konkurensi.
count
Meskipun metode ini dapat dipanggil dengan aman dari utas yang berjalan bersamaan, utas yang berbeda dapat menerima hasil yang berbeda jika nilai baru secara bersamaan dimasukkan ke dalam kontainer.
Tabel berikut menunjukkan metode dan operator yang umum digunakan yang tidak aman konkurensi.
Selain metode ini, metode apa pun yang dimulai dengan unsafe_
juga tidak aman konkurensi.
[Atas]
Kelas concurrent_unordered_multimap
Kelas konkurensi::concurrent_unordered_multimap sangat menyerupai concurrent_unordered_map
kelas kecuali memungkinkan beberapa nilai untuk memetakan ke kunci yang sama. Ini juga berbeda dari concurrent_unordered_map
dengan cara-cara berikut:
Metode concurrent_unordered_multimap::insert mengembalikan iterator alih-alih
std::pair<iterator, bool>
.Kelas
concurrent_unordered_multimap
tidak menyediakanoperator[]
atauat
metode .
Contoh berikut menunjukkan struktur dasar untuk menggunakan concurrent_unordered_multimap
. Contoh ini menyisipkan kunci karakter dalam rentang ['a', 'i']. concurrent_unordered_multimap
mengaktifkan kunci untuk memiliki beberapa nilai.
// 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]
*/
[Atas]
Kelas concurrent_unordered_set
Kelas konkurensi::concurrent_unordered_set sangat menyerupai concurrent_unordered_map
kelas kecuali mengelola nilai alih-alih pasangan kunci dan nilai. Kelas concurrent_unordered_set
tidak menyediakan operator[]
atau at
metode .
Contoh berikut menunjukkan struktur dasar untuk menggunakan concurrent_unordered_set
. Contoh ini menyisipkan nilai karakter dalam rentang ['a', 'i']. Aman untuk melakukan penyisipan secara paralel.
// 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]
*/
[Atas]
Kelas concurrent_unordered_multiset
Kelas konkurensi::concurrent_unordered_multiset sangat menyerupai concurrent_unordered_set
kelas kecuali memungkinkan nilai duplikat. Ini juga berbeda dari concurrent_unordered_set
dengan cara-cara berikut:
Metode concurrent_unordered_multiset::insert mengembalikan iterator alih-alih
std::pair<iterator, bool>
.Kelas
concurrent_unordered_multiset
tidak menyediakanoperator[]
atauat
metode .
Contoh berikut menunjukkan struktur dasar untuk menggunakan concurrent_unordered_multiset
. Contoh ini menyisipkan nilai karakter dalam rentang ['a', 'i']. concurrent_unordered_multiset
memungkinkan nilai terjadi beberapa kali.
// 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]
*/
[Atas]
Kelas yang dapat dikombinasikan
Kelas konkurensi::combinable menyediakan penyimpanan lokal utas yang dapat digunakan kembali yang memungkinkan Anda melakukan komputasi terperinci dan kemudian menggabungkan komputasi tersebut ke dalam hasil akhir. Anda dapat menganggap combinable
objek sebagai variabel pengurangan.
Kelas combinable
ini berguna ketika Anda memiliki sumber daya yang dibagikan di antara beberapa utas atau tugas. Kelas ini combinable
membantu Anda menghilangkan status bersama dengan menyediakan akses ke sumber daya bersama dengan cara bebas kunci. Oleh karena itu, kelas ini menyediakan alternatif untuk menggunakan mekanisme sinkronisasi, misalnya, mutex, untuk menyinkronkan akses ke data bersama dari beberapa utas.
Metode dan Fitur
Tabel berikut menunjukkan beberapa metode penting kelas combinable
. Untuk informasi selengkapnya tentang semua combinable
metode kelas, lihat Kelas yang dapat dikombinasikan.
Metode | Deskripsi |
---|---|
Lokal | Mengambil referensi ke variabel lokal yang terkait dengan konteks utas saat ini. |
hapus | Menghapus semua variabel thread-local dari combinable objek. |
Menggabungkan combine_each |
Menggunakan fungsi gabungan yang disediakan untuk menghasilkan nilai akhir dari kumpulan semua komputasi thread-local. |
Kelas combinable
adalah kelas templat yang diparameterkan pada hasil gabungan akhir. Jika Anda memanggil konstruktor default, T
jenis parameter templat harus memiliki konstruktor default dan konstruktor salinan. T
Jika jenis parameter templat tidak memiliki konstruktor default, panggil versi konstruktor yang kelebihan beban yang mengambil fungsi inisialisasi sebagai parameternya.
Anda dapat menyimpan data tambahan dalam combinable
objek setelah memanggil metode gabungkanatau combine_each. Anda juga dapat memanggil combine
metode dan combine_each
beberapa kali. Jika tidak ada nilai lokal dalam objek yang combinable
berubah, combine
metode dan combine_each
menghasilkan hasil yang sama setiap kali dipanggil.
Contoh
Untuk contoh tentang cara menggunakan combinable
kelas , lihat topik berikut:
Cara: Menggunakan yang dapat dikombinasikan untuk Meningkatkan Performa
Cara: Menggunakan yang dapat dikombinasikan untuk Menggabungkan Set
[Atas]
Topik Terkait
Cara: Menggunakan Kontainer Paralel untuk Meningkatkan Efisiensi
Memperlihatkan cara menggunakan kontainer paralel untuk menyimpan dan mengakses data secara efisien secara paralel.
Cara: Menggunakan yang dapat dikombinasikan untuk Meningkatkan Performa
Menunjukkan cara menggunakan combinable
kelas untuk menghilangkan status bersama, dan dengan demikian meningkatkan performa.
Cara: Menggunakan yang dapat dikombinasikan untuk Menggabungkan Set
Memperlihatkan cara menggunakan combine
fungsi untuk menggabungkan kumpulan data thread-local.
Parallel Patterns Library (PPL)
Menjelaskan PPL, yang menyediakan model pemrograman imperatif yang mempromosikan skalabilitas dan kemudahan penggunaan untuk mengembangkan aplikasi bersamaan.
Referensi
Kelas concurrent_unordered_map
Kelas concurrent_unordered_multimap
Kelas concurrent_unordered_set
Saran dan Komentar
https://aka.ms/ContentUserFeedback.
Segera hadir: Sepanjang tahun 2024 kami akan menghentikan penggunaan GitHub Issues sebagai mekanisme umpan balik untuk konten dan menggantinya dengan sistem umpan balik baru. Untuk mengetahui informasi selengkapnya, lihat:Kirim dan lihat umpan balik untuk