it-swarm-id.com

Apakah ada wadah bersamaan di C++ 11?

Secara khusus, saya mencari antrian pemblokiran. Apakah ada hal seperti itu di C++ 11? Jika tidak, apa pilihan saya yang lain? Saya benar-benar tidak ingin turun ke tingkat utas lagi. Terlalu rentan kesalahan.

50
fredoverflow

Menurut Diego Dagum dari Microsoft Visual C++ Team :

Pertanyaan berulang (yah, salah satu dari banyak) adalah tentang wadah STL dan apakah mereka aman utasnya.

Mengambil kata-kata Stephan di sini, kenyataannya adalah bahwa kata-kata itu tidak, bukan sebagai bug tetapi sebagai fitur: memiliki fungsi setiap anggota setiap STL kontainer yang mendapatkan kunci internal akan memusnahkan kinerja. Sebagai tujuan umum, perpustakaan yang sangat dapat digunakan kembali, itu tidak benar-benar memberikan kebenaran salah: level yang benar untuk mengunci adalah ditentukan oleh apa yang dilakukan program. Dalam pengertian itu, individu fungsi anggota tidak cenderung tingkat yang benar.

Perpustakaan Pola Paralel (PPL) mencakup beberapa wadah yang menyediakan akses aman ke elemen-elemen mereka:

  • Kelas concurrent_vector adalah kelas kontainer urutan yang memungkinkan akses acak ke elemen apa pun. Ini memungkinkan penambahan konkurensi, akses elemen, akses iterator, dan operasi traversal iterator.
  • Kelas concurrent_queue adalah kelas kontainer urutan yang memungkinkan akses masuk pertama, keluar pertama ke elemen-elemennya. Ini memungkinkan serangkaian operasi aman-konkurensi terbatas, seperti Push dan try_pop, untuk beberapa nama.

Beberapa sampel di sini .

Juga menarik: http://www.justsoftwaresolutions.co.uk/threading/implementing-a-thread-safe-queue-using-condition-variables.html .

36
Lior Kogan

C++ 11 tidak menyediakan wadah bersamaan dengan sendirinya. Namun, ada opsi perpustakaan . Selain PPL yang telah disebutkan, jangan lupa perpustakaan Intel TBB.

Ini memiliki implementasi queue, hash_map, set dan vector bersamaan. Tapi ini bukan hanya perpustakaan kontainer yang aman, tetapi juga dilengkapi dengan versi paralel dari algoritma standar (untuk-loop, kurangi, sortir, ...).

Situs web Intel TBB

10
Lars K.

Saya terkejut bahwa tidak ada yang menyebutkan moodycamel :: ConcurrentQueue . Kami telah menggunakannya untuk beberapa waktu dan berkinerja sangat baik. Khususnya implementasinya bebas dari penguncian, yang segera membawa kecepatan luar biasa. Alasan lain untuk menggunakannya (mengutip dari situs resmi):

Tidak banyak antrian kunci-bebas penuh untuk C++. Dorongan memiliki satu, tetapi terbatas pada objek dengan operator penugasan sepele dan destruktor sepele, misalnya. Antrian TBB Intel bukan bebas kunci, dan memerlukan konstruktor sepele juga. Ada banyak makalah akademik yang menerapkan antrian bebas kunci di C++, tetapi dapat digunakan kode sumber sulit ditemukan, dan tes lebih dari itu.

Beberapa tolok ukur dan perbandingan tersedia sini , sini dan sini .

7
Miljen Mikic

Antarmuka kontainer tidak dirancang dengan tujuan ini. Untuk antarmuka yang mereka gunakan, kunci yang terlihat oleh klien benar-benar satu-satunya cara Anda bisa melakukan ini sambil menjamin kebenaran dan perilaku yang dapat diprediksi. Ini juga akan sangat tidak efisien karena jumlah akuisisi akan sangat tinggi (relatif terhadap implementasi yang baik).

Solusi 1

Lewati nilai (jika berlaku).

Solusi 2

Buat koleksi implementasi baut sederhana yang dapat Anda gunakan untuk melewati kontainer sambil memegang kunci ruang lingkup (pertimbangkan pseudo c ++):

template <typename TCollection>
class t_locked_collection {
public:
    t_locked_collection(TCollection& inCollection, t_lock& lock) : collection(inCollection), d_lock(lock), d_nocopy() {
    }

    TCollection& collection;
    // your convenience stuff
private:
    t_scope_lock d_lock;
    t_nocopy d_nocopy;
};

kemudian penelepon memasangkan kunci dengan koleksi, dan kemudian Anda memperbarui antarmuka Anda untuk menggunakan (lewat) jenis wadah yang sesuai. Itu hanya perpanjangan kelas orang miskin.

Wadah yang dikunci ini adalah salah satu contoh sederhana, dan ada beberapa varian lainnya. Ini adalah rute yang saya pilih karena itu benar-benar memungkinkan Anda untuk menggunakan tingkat granularity yang ideal untuk program Anda, meskipun tidak transparan (secara sintaksis) sebagai metode yang dikunci. Ini juga relatif mudah untuk mengadaptasi program yang ada. Setidaknya berperilaku dengan cara yang dapat diprediksi, tidak seperti koleksi dengan kunci internal.

Varian lain adalah:

template <typename TCollection>
class t_lockable_collection {
public:
// ...
private:
    TCollection d_collection;
    t_mutex d_mutex;
};

// example:
typedef t_lockable_collection<std::vector<int> > t_lockable_int_vector;

... di mana jenis yang mirip dengan t_locked_collection dapat digunakan untuk mengekspos koleksi yang mendasarinya. Bukan untuk menyiratkan bahwa pendekatan itu sangat mudah, hanya tahan saja.

1
justin

Tidak ada wadah bersamaan di C++ 11. 

Tetapi kelas header berikut menyediakan antrian bersamaan, tumpukan dan kontainer prioritas menggunakan std :: deque.

BlockingCollection adalah kelas koleksi aman C++ 11 thread yang dimodelkan setelah kelas .NET BlockingCollection.

0
gm127

Versi saya dari peta tidak berturutan bersamaan. concurrency namespace {

template<typename T,typename T1>
class unordered_bucket: private std::unordered_map<T,T1>
{
mutable std::recursive_mutex m_mutex;

public:
T1 &operator [](T a)
{
    std::lock_guard<std::recursive_mutex> l(m_mutex);
    return std::unordered_map<T,T1>::operator [](a);
}

size_t size() const noexcept {
    std::lock_guard<std::recursive_mutex> l(m_mutex);
    return  std::unordered_map<T,T1>::size();
}

vector<pair<T,T1>> toVector() const
{
    std::lock_guard<std::recursive_mutex> l(m_mutex);

    vector<pair<T,T1>> ret;
    for(const pair<T,T1> &p:*this)
    {
        ret.Push_back(p);
    }
    return ret;
}

bool find(const T &t) const
{
    std::lock_guard<std::recursive_mutex> l(m_mutex);
    if(this->std::unordered_map<T,T1>::find(t) == this->end())
        return false;  //not found
    return true;
}
void erase()
{
    std::lock_guard<std::recursive_mutex> l(m_mutex);
    this->unordered_map<T,T1>::erase(this->begin(),this->end());
}
void erase(const T &t)
{
    std::lock_guard<std::recursive_mutex> l(m_mutex);
    this->unordered_map<T,T1>::erase(t);
}
};

#define BUCKETCOUNT 10
template<typename T,typename T1>
class ConcurrentMap
{
std::vector<unordered_bucket<T,T1>> m_v;
public:
ConcurrentMap():m_v(BUCKETCOUNT){}   //using 10 buckets

T1 &operator [](T a)
{
    std::hash<T> h;
    return m_v[h(a)%BUCKETCOUNT][a];
}

size_t size() const noexcept {
    size_t cnt=0;

    for(const unordered_bucket<T,T1> &ub:m_v)
        cnt=cnt+ub.size();

    return  cnt;
}

vector<pair<T,T1>> toVector() const
{
    vector<pair<T,T1>> ret;
    for(const unordered_bucket<T,T1> &u:m_v)
    {
        const vector<pair<T,T1>> &data=u.toVector();
        ret.insert(ret.end(),data.begin(),data.end());
    }
    return ret;
}

bool find(const T &t) const
{
    for(const unordered_bucket<T,T1> &u:m_v)
        if(true == u.find(t))
            return true;
    return false;
}
void erase()
{
    for(unordered_bucket<T,T1> &u:m_v)
        u.erase();
}
void erase(const T &t)
{
    std::hash<T> h;
    unordered_bucket<T,T1> &ub = m_v[h(t)%BUCKETCOUNT];
    ub.erase(t);
}
};
}
0
Asif Bahrainwala