Yazılım mimarisindeki modeller: aktif nesne

Adanali

Active member
Yazılım mimarisindeki modeller: aktif nesne


  1. Yazılım mimarisindeki modeller: aktif nesne

Modeller, modern yazılımın geliştirilmesinde ve yazılımın mimarisinde önemli bir soyutlamadır. Açıkça tanımlanmış terminoloji, temiz dokümantasyon ve öğrenme en iyisinden sunarlar. Yarışma modellerine ait aktif nesne, bir Wikipedia öğesini aşağıdaki gibi açıklar: “Aktif nesnelerin planlama modeli, her biri kendi kontrol iş parçacığında bulunan nesneler için yöntemin çağrılmasından yöntemin yürütülmesini yener.. “











Etkin nesne, yöntemin çağrısını yöntemin yürütülmesinden çıkarır. Yöntem çağrısı, iş parçacığı istemcisi üzerinde gerçekleştirilir, ancak yöntemin etkin nesne üzerindeki yürütülmesi. Etkin nesnenin iş parçacığı ve yapılacak yöntem gereksinimlerinin (kısa istekler) bir listesi vardır. Müşterinin yöntemi aktif nesneler listesinde arar. İstekler sunucuya gönderilecektir.








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.







Rekabet Modelleri


Aktif nesne, rekabet konusu olarak da bilinir

sorun


Birçok iplik ortak bir nesneyi senkronize ederse, aşağıdaki zorluklar çözülmelidir:



  • Yoğun bir işleme elemanı işlevini çağıran bir iş parçacığı, aynı nesneyi çok uzun süre hatırlatan diğer iş parçacıklarını engellememelidir.
  • Ortak bir nesneye erişimin senkronize edilmesi kolay olmalıdır.
  • Yapılan isteklerin rekabet özellikleri, belirli donanım ve yazılım için özelleştirilebilir olmalıdır.
Çözüm

  • İstemci yönteminin çağrısı, etkin nesnenin arayüzünü temsil eden bir milletvekiline gider.
  • Sunucu, bu üyenin işlevlerini uygular ve iş parçacığında aktif nesneler gerçekleştirir. Sonunda, yardımcısı çağrıyı sunucu yönteminden bir çağrıya dönüştürür. Bu istek, aktivasyon listesine zamanlayıcıya eklenir.
  • Zamanlayıcı olaylarının döngüsü aynı sunucu iş parçacığında çalışır, istekleri etkinleştirme listesinden kaldırır, bunları kaldırır ve sunucuya gönderir.
  • Müşteri, yardımcı milletvekilinin geleceği aracılığıyla yöntemin çağrısının sonucunu alır.
Bileşenler


Aktif nesne modeli altı bileşenden oluşur:

  • . Mengene (Proxy), aktif nesnelerin erişilebilir üyelerinin işlevleri için bir arayüz sunar. Milletvekili, aktivasyon listesinde bir soruşturma oluşturulması ile karşılaşır. Milletvekili, iş parçacığı istemcisinde yapılır.
  • . Yöntem Gereksinimleri Sınıfı (Yöntem isteği) Etkin bir nesnede gerçekleştirilen yöntemin arayüzünü tanımlar. Bu arayüz ayrıca siparişin yürütülmeye hazır olup olmadığını gösteren koruma yöntemleri de içerir. İstek, daha sonra kullanılması gereken tüm bağlam bilgilerini içerir.
  • . Aktivasyon listesi (Aktivasyonlar Listesi) Sonraki istekleri yönetir. Etkinleştirme listesi, istemci iş parçacığını etkin nesne iş parçacığından ayırır. Milletvekili gereksinim nesnesini ekler ve zamanlayıcı tekrar kaldırır. Bu nedenle, aktivasyon listesine erişim serileştirilmelidir.
  • . Zamanlayıcı Etkin nesnelerin iş parçacığında çalışır ve etkinleştirme listesinden hangi isteğin gerçekleştirileceğine karar verir. Zamanlayıcı, talebin gerçekleştirilip gerçekleştirilemeyeceğini belirleme talebini değerlendirir.
  • . sunucu Etkin nesneyi uyguladı ve iş parçacığında etkin nesneler gerçekleştirir. Sunucu, yöntem isteğinin arayüzünü uygular ve zamanlayıcı üyenin işlevlerini çağırır.
  • Milletvekili, Gelecek. Bu yalnızca talep bir sonuç sağlarsa gereklidir. Bu nedenle, istemci geleceği alır ve yöntemin etkin nesneye çağrısının sonucunu hatırlayabilir. Müşteri sonucu bekleyebilir veya sorgulayabilir.
Dinamik davranış


Aktif nesnenin dinamik davranışı üç aşamadan oluşur:

  1. Soruşturma ve planlama yaratma: Müşteri bir yardımcı yöntem gerektirir. Milletvekili bir soruşturma yaratır ve bunu zamanlayıcıya iletir. Zamanlayıcı, etkinleştirme listesindeki isteği içerir. Buna ek olarak, talep bir sonuç sağlarsa, yardımcısı müşteriye bir geleceği iade eder.
  2. Üye işlevinin yürütülmesi: Zamanlayıcı, isteğin istek yöntemini değerlendirerek hangi isteğin gerçekleştirilebileceğini belirler. Etkinleştirme listesinden isteği açın ve sunucuya gönderin.
  3. tamamlama: Talep bir sonuç sağlarsa, gelecekte kaydedilir. Müşteri sonuç hakkında bilgi isteyebilir. Müşterinin sonucu varsa, istek ve gelecek ortadan kaldırılabilir.
Aşağıdaki şekil haber dizisini göstermektedir.









Avantajlar ve dezavantajlar


Aktif nesnenin minimum bir örneğini sunmadan önce, avantajlarının ve dezavantajlarının kısa bir listesi:

Avantajlar

  • Senkronizasyon yalnızca etkin nesnenin iş parçacığında gereklidir, ancak istemci iş parçacığında değildir.
  • İstemci (kullanıcı) ve sunucu (uygulama) arasındaki ayrımı silin. Senkronizasyonun zorlukları aktüatörün yanındadır.
  • Araştırmaların eşzamansız yürütülmesi nedeniyle sistemin üretiminde artış. Delaboration -Yoğun üye işlevi çağrısı tüm sistemi engellemez.
  • Zamanlayıcı, bir sonraki talepleri yerine getirmek için çeşitli stratejiler uygulayabilir. Bu durumda, siparişler kuyrukta oldukları dışında bir sırayla gerçekleştirilebilir.
Dezavantajlar

  • İstekler çok iyiyse, aktif yardımcıs, aktivasyon listesi ve zamanlayıcı gibi aktif nesneler modelinin performansı (performansın davranışı) aşırı derecede yüksek olabilir.
  • Zamanlayıcı stratejisi ve işletim sistemi planlaması nedeniyle, aktif nesnedeki sorunların çözümü genellikle zordur. Siparişin yürütülmesi emri, emrin sağlanmasından farklısa, bu özellikle geçerlidir.
Örnek


Aşağıdaki örnek, aktif nesnelerin basitleştirilmiş bir uygulamasını göstermektedir. Özellikle, yardımcı nesne için yöntemin gereksinimleri için bir arayüz tanımlamıyorum, yardımcısı ve sunucunun uygulaması gereken. Buna ek olarak, zamanlayıcı sorulduğunda bir sonraki çalışmayı gerçekleştirir ve etkin nesnenin Run üyesinin işlevi iş parçacıklarını oluşturur.

İlgili veri türleri future<vector<future<pair<bool, int>>>> genellikle başarısız olur. Okunabilirliği artırmak için, ifadeleri güçlü bir şekilde kullandım (satır 1). Bu örnek, C ++ 'da ünlüler ve gelecek hakkında bilgi gerektirir. Ödevlerle ilgili eşyalarımda daha fazla ayrıntı var.



// activeObject.cpp

#include <algorithm>
#include <deque>
#include <functional>
#include <future>
#include <iostream>
#include <memory>
#include <mutex>
#include <numeric>
#include <random>
#include <thread>
#include <utility>
#include <vector>

using std::async; // (1)
using std::boolalpha;
using std::cout;
using std::deque;
using std::distance;
using std::for_each;
using std::find_if;
using std::future;
using std::lock_guard;
using std::make_move_iterator;
using std::make_pair;
using std::move;
using std::mt19937;
using std::mutex;
using std::packaged_task;
using std::pair;
using std::random_device;
using std::sort;
using std::jthread;
using std::uniform_int_distribution;
using std::vector;

class IsPrime { // (8)
public:
IsPrime(int num): numb{num} {}
pair<bool, int> operator()() {
for (int j = 2; j * j <= numb; ++j){
if (numb % j == 0) return make_pair(false, numb);
}
return make_pair(true, numb);
}
private:
int numb;
};

class ActiveObject {
public:

future<pair<bool, int>> enqueueTask(int i) {
IsPrime isPrime(i);
packaged_task<pair<bool, int>()> newJob(isPrime);
auto isPrimeFuture = newJob.get_future();
{
lock_guard<mutex> lockGuard(activationListMutex);
activationList.push_back(move(newJob)); // (6)
}
return isPrimeFuture;
}

void run() {
std::jthread j([this] { // (12)
while ( !runNextTask() ); // (13)
});
}

private:

bool runNextTask() { // (14)
lock_guard<mutex> lockGuard(activationListMutex);
auto empty = activationList.empty();
if (!empty) { // (15)
auto myTask= std::move(activationList.front());
activationList.pop_front();
myTask();
}
return empty;
}

deque<packaged_task<pair<bool, int>()>> activationList; //(7)
mutex activationListMutex;
};

vector<int> getRandNumbers(int number) {
random_device seed;
mt19937 engine(seed());
uniform_int_distribution<> dist(1'000'000, 1'000'000'000); // (4)
vector<int> numbers;
for (long long i = 0 ; i < number; ++i) numbers.push_back(dist(engine));
return numbers;
}

future<vector<future<pair<bool, int>>>> getFutures(ActiveObject& activeObject,
int numberPrimes) {
return async([&activeObject, numberPrimes] {
vector<future<pair<bool, int>>> futures;
auto randNumbers = getRandNumbers(numberPrimes); // (3)
for (auto numb: randNumbers){
futures.push_back(activeObject.enqueueTask(numb)); // (5)
}
return futures;
});
}


int main() {

cout << boolalpha << 'n';

ActiveObject activeObject;

// a few clients enqueue work concurrently // (2)
auto client1 = getFutures(activeObject, 1998);
auto client2 = getFutures(activeObject, 2003);
auto client3 = getFutures(activeObject, 2011);
auto client4 = getFutures(activeObject, 2014);
auto client5 = getFutures(activeObject, 2017);

// give me the futures // (9)
auto futures = client1.get();
auto futures2 = client2.get();
auto futures3 = client3.get();
auto futures4 = client4.get();
auto futures5 = client5.get();

// put all futures together // (10)
futures.insert(futures.end(),make_move_iterator(futures2.begin()),
make_move_iterator(futures2.end()));

futures.insert(futures.end(),make_move_iterator(futures3.begin()),
make_move_iterator(futures3.end()));

futures.insert(futures.end(),make_move_iterator(futures4.begin()),
make_move_iterator(futures4.end()));

futures.insert(futures.end(),make_move_iterator(futures5.begin()),
make_move_iterator(futures5.end()));

// run the promises // (11)
activeObject.run();

// get the results from the futures
vector<pair<bool, int>> futResults;
futResults.reserve(futures.size());
for (auto& fut: futures) futResults.push_back(fut.get()); // (16)

sort(futResults.begin(), futResults.end()); // (17)

// separate the primes from the non-primes
auto prIt = find_if(futResults.begin(), futResults.end(),
[](pair<bool, int> pa){ return pa.first == true; });

cout << "Number primes: " << distance(prIt, futResults.end()) << 'n'; // (19)
cout << "Primes:" << 'n';
for_each(prIt, futResults.end(), [](auto p){ cout << p.second << " ";} ); // (20)

cout << "nn";

cout << "Number no primes: " << distance(futResults.begin(), prIt) << 'n'; // (18)
cout << "No primes:" << 'n';
for_each(futResults.begin(), prIt, [](auto p){ cout << p.second << " ";} );

cout << 'n';

}


Örneğin temel fikri, müşterilerin siparişleri aynı anda aktivasyonlar listesindeki hizalayabilmesidir. Sunucu, hangi sayıların asal sayılar olduğunu ve aktivasyon listesinin etkin nesnelerin bir parçası olduğunu belirler. Etkin nesne, ayrı bir iş parçacığında etkinleştirme listesine sipariş verir ve istemciler sonuçları sorgulayabilir.

İşte detaylar


Beş müşteri işlevde iş (satır 2) sağlar getFutures Kuyruğunda activeObjects. getFutures Al activeObject Ve bir numara numberPrimes aksine. numberPrimes Arasında oluşturulan rastgele sayılar (satır 3) 1'000'000 VE 1'000'000'000 (Satır 4) ve onu dönüş değerine iter: vector<future<pair<bool, int>>. future<pair<bool, int> Bir bool Ve int. . bool Numaranın ilk sayı olup olmadığını gösterir. Hatta daha yakından bakalım (5): futures.push_back(activeObject.enqueueTask(numb)). Bu çağrı, etkinleştirme listesine (satır 6) yeni bir görevin dahil edilmesini tetikler. Aktivasyon listesindeki tüm çağrılar korunmalıdır. Aktivasyon listesi bir deque vaatlerin (satır 7): deque<packaged_task<pair<bool, int>()>>. Her vaat, çağrı sırasında işlevsel nesneyi yönlendirir IsPrime (Riga 8). Dönüş değeri birkaç bool ve bir int. . bool sayının olup olmadığını gösterir int İlk sayıdır.

Şimdi çalışma paketleri hazırlanıyor, hesaplama başlayabilir. Tüm müşteriler tutamaçları (9) ile ilişkili vadeli işlemlere iade eder. Tüm vadeli işlemlerin (satır 10) kombinasyonu işi basitleştirir. Arama activeObject.run() İnfaz hatta başlar (11). Üye işlevi run (Satır 12) Üye işlevine iş parçacığını oluşturun runNextTask (Satır 13). runNextTask (Satır 14) deque Boş değil (satır 15) ve yeni görevi oluşturur. Çağrı futResults.push_back(fut.get()) (Satır 16) Tüm sonuçlar talep edildi ve her gelecekte futResults itti. Çizgi (17) Çiftler Taşıyıcısı Sipariş verir: vector<pair<bool, int>>. Hesaplama kalan satırlarda gösterilir. Yineleyici prIt (18) satırında, ilk numaraya sahip bir çiftin ilk yineleyicisini içerir.

Aşağıdaki ekran görüntüsü, asal sayıların sayısını göstermektedir distance(prIt, futResults.end()) (Satır 19) ve asal sayılar (satır 20). Yalnızca ilk numaralar görüntülenmez:









Sırada ne var?


Etkin nesneyi ve monitör nesnesini senkronize edin ve üyelerin üyelerini aramayı planlayın. Temel fark, etkin nesnenin üye işlevini başka bir iş parçacığında gerçekleştirmesidir, monitör nesnesi istemci ile aynı iş parçacığında. Bir sonraki makalemde Monitör nesnesini daha kesin olarak tanıtacağım.


(harita)
 
Üst