Yazılım Geliştirme – Yüz Değişimi: Kendinizi Engelleme

Adanali

Active member
Yazılım Geliştirme – Yüz Değişimi: Kendinizi Engelleme


  1. Yazılım Geliştirme – Yüz Değişimi: Kendinizi Engelleme

Modeller, modern yazılımın geliştirilmesinde önemli bir soyutlamadır. Açıkça tanımlanmış terminoloji, temiz dokümantasyon ve öğrenme en iyisinden sunarlar. Bu makale rekabet modellerini yönetmeye devam etmektedir. Blok, ortak ve değişen bir durumu korumak için klasik bir yöntemdir. Bugün iki varyant sunuyorum: açık ve stratejik kilitleme ile bloke.










Rainer Grimm yıllardır yazılım mimarı, ekip ve eğitim müdürü olarak çalıştı. C ++ programlama dilleri, Python ve Haskell hakkında makaleler yazmayı seviyor, ancak uzman konferanslarla konuşmayı da seviyor. Modern C ++ blogunda, C ++ tutkusuyla yoğun bir şekilde ilgileniyor.













Blok kritik bir bölümü korumak için basit bir fikre sahiptir. Kritik bir bölüm, bir iş parçacığının yalnızca kullanması gereken kodun bir parçasıdır.

Açıklanmamış blok


Kapsamlı blok, bir muteks için uygulanan RAII kavramıdır. Açıklanmış blok, senkronize bir blok ve koruma olarak da bilinir. Bu dilin temel fikri, kaynakların edinilmesini ve serbest bırakılmasını bir nesnenin ömrünün süresi ile ilişkilendirmektir. Adından da anlaşılacağı gibi, “kapsamlı” nesnenin ömrü süresidir. Scoped, C ++ teriminin nesnenin yok edilmesinden ve dolayısıyla kaynağın serbest bırakılmasından sorumlu olduğu anlamına gelir.

Sınıf ScopedLock Uygulanan kapsam ile blok.



// scopedLock.cpp

#include <iostream>
#include <mutex>
#include <new>
#include <string>

class ScopedLock{
private:
std::mutex& mut;
public:
explicit ScopedLock(std::mutex& m): mut(m){ // (1)
mut.lock(); // (2)
std::cout << "Lock the mutex: " << &mut << 'n';
}
~ScopedLock(){
std::cout << "Release the mutex: " << &mut << 'n';
mut.unlock(); // (3)
}
};

int main(){

std::cout << 'n';

std::mutex mutex1;
ScopedLock scopedLock1{mutex1};

std::cout << "nBefore local scope" << 'n';
{
std::mutex mutex2;
ScopedLock scopedLock2{mutex2};
} // (4)
std::cout << "After local scope" << 'n';

std::cout << "nBefore try-catch block" << 'n';
try{
std::mutex mutex3;
ScopedLock scopedLock3{mutex3};
throw std::bad_alloc();
} // (5)
catch (std::bad_alloc& e){
std::cout << e.what();
}
std::cout << "nAfter try-catch block" << 'n';

std::cout << 'n';

}


ScopedLock Referans için muteksini alır (1). Muteks üreticiye (2) çekilir ve Destructtor'da (3) tekrar serbest bırakılır. RAII dili sayesinde nesne yok edilir ve bu nedenle Mutex'in serbest bırakılması.



Akışı scopedLock1 Sonunda biter main-İşlev. Sonuç olarak, mutex1 kilidi açıldı. Aynı şey mutex2 VE mutex3. Yerel alanlarının sonunda otomatik olarak yayınlanırlar (4 ve 5). İLE mutex3 aynı zamanda muhrip scopedLock3 Bir istisna meydana geldiğinde arandı. İlginç mutex3 Anısı mutex2 Yeniden kullanıldı çünkü her ikisi de aynı adrese sahip.









Alanlı blok aşağıdaki avantajlara ve dezavantajlara sahiptir:

Avantajlar:

  • Sağlamlık, blok otomatik olarak elde edildiğinden ve serbest bırakıldığından.
Dezavantajlar:

  • Birinin özyinelemeli bukleleri std::mutex Belirsiz bir davranıştır ve genellikle bir çıkmaza yol açar.
  • C LongJMP işlevi kullanılırsa kilitler otomatik olarak serbest bırakılmaz; Longjpm, küreli nesnelerin C ++-yıkıcı olarak adlandırmaz.
C ++ 17, dört varyanttaki blokları destekler. C ++ std::lock_guard / std::scoped_lock Basit ve bir std::unique_lock / std::shared_lock Açık bukleler veya Mutex salınımı gibi gelişmiş kullanım durumları için. “Kilitler” makalemde daha fazla muteks ve kilitle ilgili.

Stratejik blok, dinlemeyi engellemeye başlamayı sever.

Stratejik kilitleme


Bir kitapçı gibi kodun çeşitli alanlarda yanda da kullanılması gerektiğini varsayalım. Kritik bölümleri bir blokla koruduğunuzdan emin olmak için. Kütüphane şimdi tek bir iş parçacığı ortamında çalışıyorsa, gerekli olmayan pahalı bir senkronizasyon mekanizması gerekli olmadığı için bir performans sorunu sunulur. Bu durumda, stratejik blok idealdir: strateji modelinin bloğa uygulanması. Bu, kontrol stratejinizi bir nesneye koyduğunuz ve onu sisteminizin bir bileşeni haline getirdiğiniz anlamına gelir.

Stratejik tıkanıklığın uygulanması için iki tipik yöntem, derleme sırasında (modeller) yürütme aşamasında (nesnelere yönelim) polimorfizmdir (modeller). Her iki yol da kilitleme stratejisinin uyarlanmasını ve genişlemesini geliştirir, sistemin bakımını kolaylaştırır ve bileşenlerin yeniden kullanımını destekler. Yürütme veya derleme süresi sırasında stratejik engelleme bloğunun uygulanması çeşitli açılardan farklıdır.

Avantajları:

Yürütme aşamasında polimorfizm

  • Yürütme aşaması sırasında kontrol stratejisinin yapılandırılmasına izin verir
  • Nesne yönelimli bir geçmişi olan geliştiriciler için daha kolaydır.
Derleme dönemine polimorfizm

  • soyutlama dezavantajları yoktur ve
  • Düz bir hiyerarşisi var.
Dezavantajlar:

Yürütme aşamasında polimorfizm

  • ek bir inceleme işaretçisi gerektirir
  • Derin bir türev hiyerarşisi olabilir.
Derleme dönemi için polimorfizm

  • hata durumunda uzun hata mesajları oluşturabilir (bu, gibi kavramlarla değişir BasicLockable C ++ 20'de).
Bu teorik tartışmadan sonra, stratejik bloğu her iki varyantta da uygulayacağım. Örneğimde, stratejik blok herhangi bir özel ve paylaşılan bloğu desteklemez. Basitlik için mevcut Mutex'i zaten kullandım.

Yürütme aşamasında polimorfizm


Program strategizedLockingRuntime.cpp Üç farklı kilitleme stratejisi kullanın.



// strategizedLockingRuntime.cpp

#include <iostream>
#include <mutex>
#include <shared_mutex>

class Lock { // (4)
public:
virtual void lock() const = 0;
virtual void unlock() const = 0;
};

class StrategizedLocking {
Lock& lock; // (1)
public:
StrategizedLocking(Lock& l): lock(l){ // (2)
lock.lock();
}
~StrategizedLocking(){ // (3)
lock.unlock();
}
};

struct NullObjectMutex{
void lock(){}
void unlock(){}
};

class NoLock : public Lock { // (5)
void lock() const override {
std::cout << "NoLock:🔒 " << 'n';
nullObjectMutex.lock();
}
void unlock() const override {
std::cout << "NoLock:🔓 " << 'n';
nullObjectMutex.unlock();
}
mutable NullObjectMutex nullObjectMutex; // (10)
};

class ExclusiveLock : public Lock { // (6)
void lock() const override {
std::cout << " ExclusiveLock:🔒 " << 'n';
mutex.lock();
}
void unlock() const override {
std::cout << " ExclusiveLock:🔓 " << 'n';
mutex.unlock();
}
mutable std::mutex mutex; // (11)
};

class SharedLock : public Lock { // (7)
void lock() const override {
std::cout << " SharedLock::lock_shared: " << 'n';
sharedMutex.lock_shared(); // (8)
}
void unlock() const override {
std::cout << " SharedLock::unlock_shared: " << 'n';
sharedMutex.unlock_shared(); // (9)
}
mutable std::shared_mutex sharedMutex; // (12)
};

int main() {

std::cout << 'n';

NoLock noLock;
StrategizedLocking stratLock1{noLock};

{
ExclusiveLock exLock;
StrategizedLocking stratLock2{exLock};
{
SharedLock sharLock;
StrategizedLocking startLock3{sharLock};
}
}

std::cout << 'n';

}


Sınıf StrategizedLocking Bir bloğu vardır (1). StrategizedLocking Modelleri alanla kilitleme ve daha sonra üreticideki muteks çeker (2) ve onu Destroyer'da (3) tekrar serbest bırakır. Lock (4) Soyut bir sınıftır ve türev sınıflarının arayüzünü tanımlar. Bunlar Nolock sınıfları (5), ExclusiveLock (6) e SharedLock (7). SharedLock çağrı lock_shared (8) Ve unlock_shared (9) std::shared_mutex AÇIK. Bu blokların her biri mutekslerden birini içerir NullObjectMutex (10), std::mutex (11) veya std::shared_mutex (Satır 12). NullObjectMutex Noop bir yer tutucu. Muteks olacak mutable ilan edildi. Bu nedenle, sürekli olarak üyelerin işlevleridir. lock VE unlock Kullanılabilir.

Derleme zamanları için polimorfizm


Modele dayalı uygulama nesne yönelimli uygulamaya çok benzer. Soyut bir temel sınıf yerine Lock Kavramı tanımlıyorum BasicLockable. Kavramlar hakkında daha fazla bilgi önceki makalemde mevcuttur: Kavramlar.



template <typename T>
concept BasicLockable = requires(T lo) {
lo.lock();
lo.unlock();
};


BasicLockable Türünden istekler T, o lock VE unlock uygulandı. Sonuç olarak, sınıf sınıfı kabul eder StrategizedLocking Sadece bu kısıtlamayı tatmin eden Tipparametri.



template <BasicLockable Lock>
class StrategizedLocking {
...


Son olarak, modele dayalı uygulama aşağıdadır.



// strategizedLockingCompileTime.cpp

#include <iostream>
#include <mutex>
#include <shared_mutex>

template <typename T>
concept BasicLockable = requires(T lo) {
lo.lock();
lo.unlock();
};

template <BasicLockable Lock>
class StrategizedLocking {
Lock& lock;
public:
StrategizedLocking(Lock& l): lock(l){
lock.lock();
}
~StrategizedLocking(){
lock.unlock();
}
};

struct NullObjectMutex {
void lock(){}
void unlock(){}
};

class NoLock{
public:
void lock() const {
std::cout << "NoLock:🔒 " << 'n';
nullObjectMutex.lock();
}
void unlock() const {
std::cout << "NoLock:🔓 " << 'n';
nullObjectMutex.lock();
}
mutable NullObjectMutex nullObjectMutex;
};

class ExclusiveLock {
public:
void lock() const {
std::cout << " ExclusiveLock:🔒 " << 'n';
mutex.lock();
}
void unlock() const {
std::cout << " ExclusiveLock:🔓 " << 'n';
mutex.unlock();
}
mutable std::mutex mutex;
};

class SharedLock {
public:
void lock() const {
std::cout << " SharedLock::lock_shared: " << 'n';
sharedMutex.lock_shared();
}
void unlock() const {
std::cout << " SharedLock::unlock_shared: " << 'n';
sharedMutex.unlock_shared();
}
mutable std::shared_mutex sharedMutex;
};

int main() {

std::cout << 'n';

NoLock noLock;
StrategizedLocking<NoLock> stratLock1{noLock};

{
ExclusiveLock exLock;
StrategizedLocking<ExclusiveLock> stratLock2{exLock};
{
SharedLock sharLock;
StrategizedLocking<SharedLock> startLock3{sharLock};
}
}

std::cout << 'n';

}


Programlar strategizedLockingRuntime.cpp VE strategizedLockingCompileTime.cpp Aynı sorunu yaratın:









Sırada ne var?


Korumalı süspansiyon, değişimle yüzleşmek için başka bir strateji kullanır. Değişiklik gerçekleştirildiğinde raporlayın. Bir sonraki makalemde, tutulan süspansiyon hakkında daha ayrıntılı olarak gideceğim.


(RME)
 
Üst