Nasıl C hareketli tip uyumu ile mi ilgileneyim ?
Tasarım değil hareketli ne de kopya constructable. Bu bir dışlama tutan A
, almazsınız bir sınıf-hareket-yapıcı varsayılan anlamına gelir.
Nasıl bu tip A
hareketli iş parçacığı için güvenli bir şekilde yapmak istiyorsunuz?
CEVAP
Hadi kod biraz ile başlayın:
class A
{
using MutexType = std::mutex;
using ReadLock = std::unique_lock<MutexType>;
using WriteLock = std::unique_lock<MutexType>;
mutable MutexType mut_;
std::string field1_;
std::string field2_;
public:
...
Gerçekten yararlanmak istemiyoruz orada oldukça düşündüren bazı tür adları C 11, ama çok daha yararlı olmak C 14 verdim. Sabırlı ol, oraya da geleceğiz.
Soru için aşağı kaynar:
Nasıl bu sınıf için hareket yapıcı ve hareket atama operatörü yazacağım?
Hareket kurucu ile başlayacağız.
Yapıcı Hareket
Üyesi mutex
mutable
yapılmış olduğunu unutmayın. Kesinlikle bu konuşma hareket üyeleri için gerekli değil, ama aynı zamanda kopya üyeleri istiyorum varsayıyorum. Eğer durum böyle değilse, zaman uyumu mutable
yapmaya gerek yok.
A
inşa ederken this->mut_
kilit gerekmez. Ama (Taşı veya kopyala) inşa ettiğiniz nesne mut_
kilitlemek gerekir. Bu çok gibi yapılabilir:
A(A&& a)
{
WriteLock rhs_lk(a.mut_);
field1_ = std::move(a.field1_);
field2_ = std::move(a.field2_);
}
a.mut_
kilitli sonra this
üyeleri ilk varsayılan inşa ettik, sonra da onlara değer atamak unutmayın.
Atama Taşıyın
Hareket atama operatörü ise başka bir konuyu da belirtin veya atama ifadenin oluşturduğu erişme bilmediğiniz için önemli ölçüde daha karmaşıktır. Ve genel olarak, aşağıdaki senaryoyu karşı korunmak için:
// Thread 1
x = std::move(y);
// Thread 2
y = std::move(x);
Burada doğru yukarıdaki senaryo Muhafızları harekete atama operatörü:
A& operator=(A&& a)
{
if (this != &a)
{
WriteLock lhs_lk(mut_, std::defer_lock);
WriteLock rhs_lk(a.mut_, std::defer_lock);
std::lock(lhs_lk, rhs_lk);
field1_ = std::move(a.field1_);
field2_ = std::move(a.field2_);
}
return *this;
}
Bir std::lock(m1, m2)
iki uyumu kilitlemek için, sadece onları birbiri ardına kilitleme yerine kullanmanız gerektiğini unutmayın. Eğer onları birbiri ardına kilitlerseniz, o zaman iki konu yukarıda gösterildiği gibi ters sırayla iki nesne atamak, kilitlenme alabilirsiniz. std::lock
bu kilitlenme önlemek için.
Kurucu Kopyalayın
Kopya üyeleri sormadım, ama onlar hakkında şimdi (sen değilsen, birileri onları gerekir) konuşabiliriz.
A(const A& a)
{
ReadLock rhs_lk(a.mut_);
field1_ = a.field1_;
field2_ = a.field2_;
}
Kopya yapıcı hareket ReadLock
diğer WriteLock
yerine kullanılır dışında yapıcı gibi görünüyor. Şu anda her ikisi de diğer adı aslında fark etmez std::unique_lock<std::mutex>
ve bu.
Ama C 14, bunun iki seçeneği vardır:
using MutexType = std::shared_timed_mutex;
using ReadLock = std::shared_lock<MutexType>;
using WriteLock = std::unique_lock<MutexType>;
Buolabilirbir iyileştirme olabilir, ama kesinlikle değil. Ölçmek için olup olmadığını belirlemek gerekir. Ama bu değişiklik ile, bir kopyasını inşa etmekdanbirden çok iş parçacığı aynı anda oluşturduğu. C 11 çözüm size oluşturduğu değiştirilmiş olmasa bile bu tür konuları sıralı yapmak için zorlar.
Atama Kopyalayın
Bütünlüğü için, burada her şey okuduktan sonra oldukça kendini açıklayıcı olmalıdır kopya atama operatör,
A& operator=(const A& a)
{
if (this != &a)
{
WriteLock lhs_lk(mut_, std::defer_lock);
ReadLock rhs_lk(a.mut_, std::defer_lock);
std::lock(lhs_lk, rhs_lk);
field1_ = a.field1_;
field2_ = a.field2_;
}
return *this;
}
Ve vb.
Diğer üyeler veya ücretsiz fonksiyonlara erişim A
'In durumu da eğer birden çok iş parçacığı aynı anda onları arayabilir düşünüyorsanız korunması gerekir. Örneğin, burada swap
:
friend void swap(A& x, A& y)
{
if (&x != &y)
{
WriteLock lhs_lk(x.mut_, std::defer_lock);
WriteLock rhs_lk(y.mut_, std::defer_lock);
std::lock(lhs_lk, rhs_lk);
using std::swap;
swap(x.field1_, y.field1_);
swap(x.field2_, y.field2_);
}
}
Eğer sadece std::swap
işi bağlı, yanlış kilitleme parçalı yapı, std::swap
DAHİLİ olarak gerçekleştireceği ve üç hamle arasında kilitleme kilidini unutmayın.
Gerçekten de, düşünmek swap
mı içgörü API ihtiyacın olabilir sağlamak için bir "iş parçacığı güvenli" A
, genel farklı olacak bir "non-thread-safe" API, çünkü bu "kilitleme parçalı yapı" sorunu.
Ayrıca karşı korumak için gereken "öz-swap". "öz-takas" olması bir no-op. not Kendini kontrol olmadan bir yinelemeli olarak aynı zaman uyumu kilitlerdi. Bu da MutexType
std::recursive_mutex
kullanarak öz-onay olmadan çözülebilir.
Güncelleme
Yakk aşağıda açıklamalarda kopyalama ve taşıma kurucular şeyler varsayılan inşa etme konusunda oldukça mutsuz (ve haklı). Bu konuda yeterince güçlü, hafıza harcamaya istekli öyle ki hissedesin, bu yüzden önleyebilirsiniz:
İhtiyacınız ne olursa olsun veri üyeleri olarak ekleyin. Bu üyeler korumalı veri önce gelmelidir:
mutable MutexType mut_; ReadLock read_lock_; WriteLock write_lock_; // ... other data members ...
Ve kurucular (kopya kurucu gibi) o zaman bunu:
A(const A& a) : read_lock_(a.mut_) , field1_(a.field1_) , field2_(a.field2_) { read_lock_.unlock(); }
Oops, Yakk bu güncelleştirmeyi tamamlamak için şansı vardı önce onun yorumunu sildim. Ama bu sorunu iterek, ve bu cevap bir çözüm almak için kredi hak ediyor.
Güncelleme 2
Ve dyp bu iyi bir öneri ile geldi:
A(const A& a)
: A(a, ReadLock(a.mut_))
{}
private:
A(const A& a, ReadLock rhs_lk)
: field1_(a.field1_)
, field2_(a.field2_)
{}
Nasıl 100 hareketli hedefler arasında ...
Nasıl bir elementin hareketli olmak is...
Nasıl genişletilmiş pencere çerçevesi ...
Nasıl son tamamlama Geri Al mı?...
JavaScript kilitler nasıl çalışır?...