Yazılım Geliştirme: Değişimi Kucaklayın – Güvenli Uyku

Adanali

Active member
Yazılım Geliştirme: Değişimi Kucaklayın – Güvenli Uyku


  1. Yazılım Geliştirme: Değişimi Kucaklayın – Güvenli Uyku

Modern yazılım geliştirmede önemli bir soyutlama olan şablonlar, iyi tanımlanmış terminoloji, temiz belgeler ve en iyiden öğrenme sağlar. Bu gönderi, eşzamanlılık modellerine daha da giriyor. Guarded Suspension, değişimle başa çıkmak için özel bir strateji kullanır. Değişikliğiniz bittiğinde rapor verin.








Korumalı askıya almanın temel varyantı, bir bloğu karşılanması gereken bir ön koşulla birleştirir. Önkoşul karşılanmazsa, denetim iş parçacığı uyku moduna geçer. Kontrol iş parçacığı, bir veri yarışına veya kilitlenmeye yol açabilecek bir yarış durumundan kaçınmak için bir kilit kullanır.







Rainer Grimm, uzun yıllardır yazılım mimarı, ekip lideri ve eğitim yöneticisi olarak çalışmaktadır. C++, Python ve Haskell programlama dilleri üzerine makaleler yazmaktan hoşlanır, aynı zamanda sık sık uzmanlık konferanslarında konuşmaktan da keyif alır. Modernes C++ blogunda yoğun bir şekilde C++ tutkusundan bahsediyor.







Korumalı süspansiyonun birkaç çeşidi vardır:

  • Bekleyen iş parçacığı, durum değişikliğinden pasif olarak haberdar edilebilir veya aktif olarak durum değişikliğini talep edebilir. Bu, itmeye karşı çekme ilkesine karşılık gelir.
  • Bekleme, süre sınırlaması olan veya olmayan olabilir.
  • Bildirim, bekleyen bir diziye veya tüm dizilere gönderilebilir.
Bu yazıda, Güvenli Uykunun ardındaki yalnızca kabaca fikri sunuyorum.

İtme ve çekme prensibi



İtme ilkesiyle başlamak istiyorum.

itme ilkesi


Koşul değişkenleri veya bir gelecek/vaat çifti genellikle iş parçacıklarını senkronize etmek için kullanılır. Koşul değişkeni veya söz, bekleyen iş parçacığını bilgilendirir. Bir sözün hiçbiri yoktur notify_one- VEYA notify_all-Üye işlevi. Genellikle işe yaramaz set_value-Bir bildirim sinyali vermek için kullanılan çağrı. Aşağıdaki program parçacıkları, bildirimi gönderen ileti dizisini ve bekleyen ileti dizisini gösterir.


void waitingForWork(){
std::cout << "Worker: Waiting for work." << 'n';
std::unique_lock<std::mutex> lck(mutex_);
condVar.wait(lck, []{ return dataReady; });
doTheWork();
std::cout << "Work done." << 'n';
}

void setDataReady(){
{
std::lock_guard<std::mutex> lck(mutex_);
dataReady = true;
}
std::cout << "Sender: Data is ready." << 'n';
condVar.notify_one();
}


void waitingForWork(std::future<void>&& fut){

std::cout << "Worker: Waiting for work." << std::endl;
fut.wait();
doTheWork();
std::cout << "Work done." << std::endl;

}

void setDataReady(std::promise<void>&& prom){

std::cout << "Sender: Data is ready." << std::endl;
prom.set_value();

}


çekiş prensibi


Durumun değişmesini pasif bir şekilde beklemek yerine, geliştiriciler aktif olarak bunu talep edebilir. C++ bu çekme ilkesini yerel olarak desteklemez, ancak örneğin atomik veri türleri ile uygulanabilir.


std::vector<int> mySharedWork;
std::atomic<bool> dataReady(false);

void waitingForWork(){
std::cout << "Waiting " << 'n';
while (!dataReady.load()){
std::this_thread::sleep_for(std::chrono::milliseconds(5));
}
mySharedWork[1] = 2;
std::cout << "Work done " << 'n';
}

void setDataReady(){
mySharedWork = {1, 0, 3};
dataReady = true;
std::cout << "Data prepared" << 'n';
}


Zaman kısıtlaması olan ve olmayan bekleme


Bir koşul değişkeni ve bir gelecek, beklenecek üç üye işleve sahiptir: wait, wait_for VE wait_until. ONLAR wait_for– Varyant, bir süre gerektirir ve wait_until– zaman içinde bir noktanın varyantı. Çeşitli bekleme stratejileriyle, tüketici iş parçacığı aşağıdaki kod örneğinde belirtilen süre kadar bekler steady_clock::now() + dur. Gelecek değer ister; söz henüz hazır değilse, gelecek sadece kimliğini gösterir:


void producer(promise<int>&& prom){
cout << "PRODUCING THE VALUE 2011nn";
this_thread::sleep_for(seconds(5));
prom.set_value(2011);
}

void consumer(shared_future<int> fut,
steady_clock::duration dur){
const auto start = steady_clock::now();
future_status status= fut.wait_until(steady_clock::now() + dur);
if ( status == future_status::ready ){
lock_guard<mutex> lockCout(coutMutex);
cout << this_thread::get_id() << " ready => Result: " << fut.get()
<< 'n';
}
else{
lock_guard<mutex> lockCout(coutMutex);
cout << this_thread::get_id() << " stopped waiting." << 'n';
}
const auto end= steady_clock::now();
lock_guard<mutex> lockCout(coutMutex);
cout << this_thread::get_id() << " waiting time: "
<< getDifference(start,end) << " ms" << 'n';
}


Bekleyen ileti dizilerinden birini veya tümünü bilgilendir


notify_one bekleyen ipliklerden birini uyandırır, notify_all tüm bekleyen konuları uyandır. İle notify_one hangi iş parçacığının uyandırılması gerektiğini belirtmek mümkün değildir. Uyandırılmayan ileti dizileri bekleme durumunda kalır. Birlikte std::future bu olamaz çünkü gelecek ile vaat arasında bire bir ilişki vardır. Birden çoğa bir ilişki varsa, bir std::shared_future bir yerine std::future kopyalanabildiği için kullanılır.

Aşağıdaki program, vaatler ve gelecekler arasında bire bir ve bire çok ilişkisi olan basit bir iş akışını göstermektedir.


// bossWorker.cpp

#include <future>
#include <chrono>
#include <iostream>
#include <random>
#include <string>
#include <thread>
#include <utility>

int getRandomTime(int start, int end){

std::random_device seed;
std::mt19937 engine(seed());
std::uniform_int_distribution<int> dist(start,end);

return dist(engine);
};

class Worker{
public:
explicit Worker(const std::string& n):name(n){};

void operator() (std::promise<void>&& preparedWork,
std::shared_future<void> boss2Worker){

// prepare the work and notfiy the boss
int prepareTime= getRandomTime(500, 2000);
std::this_thread::sleep_for(std::chrono::milliseconds(prepareTime));
preparedWork.set_value(); // (5)
std::cout << name << ": " << "Work prepared after "
<< prepareTime << " milliseconds." << 'n';

// still waiting for the permission to start working
boss2Worker.wait();
}
private:
std::string name;
};

int main(){

std::cout << 'n';

// define the std::promise => Instruction from the boss
std::promise<void> startWorkPromise;

// get the std::shared_future's from the std::promise
std::shared_future<void> startWorkFuture= startWorkPromise.get_future();

std::promise<void> herbPrepared;
std::future<void> waitForHerb = herbPrepared.get_future();
Worker herb(" Herb"); // (1)
std::thread herbWork(herb, std::move(herbPrepared), startWorkFuture);

std::promise<void> scottPrepared;
std::future<void> waitForScott = scottPrepared.get_future();
Worker scott(" Scott"); // (2)
std::thread scottWork(scott, std::move(scottPrepared), startWorkFuture);

std::promise<void> bjarnePrepared;
std::future<void> waitForBjarne = bjarnePrepared.get_future();
Worker bjarne(" Bjarne"); // (3)
std::thread bjarneWork(bjarne, std::move(bjarnePrepared), startWorkFuture);

std::cout << "BOSS: PREPARE YOUR WORK.n " << 'n';

// waiting for the worker
waitForHerb.wait(), waitForScott.wait(), waitForBjarne.wait(); // (4)

// notify the workers that they should begin to work
std::cout << "nBOSS: START YOUR WORK. n" << 'n';
startWorkPromise.set_value(); // (6)

herbWork.join();
scottWork.join();
bjarneWork.join();

}


Programın temel fikri, patronun (ana iş parçacığı) üç işçiye sahip olmasıdır: herb (Satır 1), scott (satır 3) e bjarne (3. satır). Her çalışan bir iş parçacığı ile temsil edilir. Satır (4)’te patron, tüm işçiler iş paketlerini hazırlamayı bitirene kadar bekler. Bu, her işçinin işini bitirdikten belirli bir süre sonra mesajı patrona göndermesi anlamına gelir. İşçiyi patrona bildirmek onlar gibi bire bir ilişkidir. std::future kullanılmış (satır 5). Buna karşılık, işe başlama sırası, patrondan çalışanlarına kadar bire çok ilişkidir (6. satır). Bu birden çoğa bildirim için bir std::shared_future gerekli.








Küçük bir yaz tatili


Önümüzdeki birkaç hafta için kısa bir yaz tatili yapıyorum ve blog yazısı paylaşmıyorum. Bir sonraki yazım 19 Haziran’da çıkacak.

Temiz kod eğitimi



(harita)



Haberin Sonu
 
Üst