diff --git a/README.md b/README.md index 4653416..a5689b8 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -### Erklärung der Praktikumsaufgabe (Synchronisation) +/* ### Erklärung der Praktikumsaufgabe (Synchronisation) #### **Kernziel der Aufgabe** Sie sollen ein **multithreaded Sensornetzwerk** simulieren, das drei Komponenten umfasst: @@ -151,4 +151,5 @@ Starten Sie das System mit verschiedenen Parametern und beobachten Sie: Mit dieser Struktur erfüllen Sie alle Lernziele: ✅ Reader-Writer-Problem ✅ Producer-Consumer-Pattern -✅ Vermeidung von Race Conditions & Deadlocks! \ No newline at end of file +✅ Vermeidung von Race Conditions & Deadlocks! +*/ \ No newline at end of file diff --git a/analysis_model.h b/analysis_model.h index 111ddbe..4b4be10 100644 --- a/analysis_model.h +++ b/analysis_model.h @@ -2,28 +2,50 @@ #include #include +/** + * Implementiert das Reader-Writer Problem mit: + * - Mehrere gleichzeitige Leser + * - Exklusiver Zugriff für Schreiber + * - Verhindert Writer-Starvation + */ class AnalysisModel { - int value = 0; - int reader_count = 0; + int value = 0; // Das geteilte Analysemodell (vereinfacht) + int reader_count = 0; // Zählt aktive Leser - std::mutex model_mutex; - std::mutex count_mutex; - std::condition_variable no_writer; + // Synchronisationsprimitive + std::mutex model_mutex; // Schützt Schreibzugriffe (exklusiv) + std::mutex count_mutex; // Schützt Leserzähler + std::condition_variable no_writer; // Garantiert Fairness public: + /** + * Lesender Zugriff + * @return Aktueller Wert des Modells + * + * Funktionsweise: + * 1. Sperrt count_mutex und inkrementiert reader_count + * 2. Erster Leser sperrt model_mutex (blockiert Writer) + * 3. Entsperrt count_mutex während des Lesens + * 4. Liest Wert + * 5. Sperrt count_mutex zum Dekrementieren + * 6. Letzter Leser entsperrt model_mutex und benachrichtigt Writer + */ int read() { std::unique_lock count_lock(count_mutex); reader_count++; + // Erster Leser sperrt für Writer if(reader_count == 1) { model_mutex.lock(); } count_lock.unlock(); + // Kritischer Abschnitt (Lesen, kann parallel erfolgen) int result = value; count_lock.lock(); reader_count--; + // Letzter Leser gibt für Writer frei if(reader_count == 0) { model_mutex.unlock(); no_writer.notify_one(); @@ -32,12 +54,22 @@ public: return result; } + /** + * Schreibender Zugriff + * @param new_value Neuer Wert für das Modell + * + * Funktionsweise: + * 1. Sperrt model_mutex (exklusiver Zugriff) + * 2. Schreibt neuen Wert + * 3. Wartet bis alle Leser fertig sind (Starvation Prevention) + */ void write(int new_value) { std::unique_lock lock(model_mutex); value = new_value; + // Verhindert Writer-Starvation no_writer.wait(lock, [this]() { return reader_count == 0; }); } -}; +}; \ No newline at end of file diff --git a/main.cpp b/main.cpp index 9b2ae67..b608ad5 100644 --- a/main.cpp +++ b/main.cpp @@ -4,11 +4,16 @@ #include #include +// Standardkonfiguration constexpr size_t DEFAULT_NUM_SENSORS = 3; constexpr size_t DEFAULT_NUM_ANALYSERS = 2; -constexpr int DEFAULT_RUN_TIME = 30; +constexpr int DEFAULT_RUN_TIME = 30; // Sekunden constexpr size_t DEFAULT_BUFFER_SIZE = 8; +/** + * Führt die Simulation mit gegebenen Parametern aus + * @tparam N Puffergröße + */ template void run_simulation(size_t num_sensors, size_t num_analysers, int run_time) { SensorNetwork network; @@ -25,13 +30,21 @@ void run_simulation(size_t num_sensors, size_t num_analysers, int run_time) { std::cout << "\n=== Simulation beendet ===\n"; } +/** + * Liest Benutzereingabe mit Standardwert + * @param prompt Eingabeaufforderung + * @param default_value Standardwert bei leerer Eingabe + * @return Eingegebener oder Standardwert + */ size_t get_input(const std::string& prompt, size_t default_value) { std::cout << prompt << " [" << default_value << "]: "; std::string input; std::getline(std::cin, input); + // Verwende Standardwert bei leerer Eingabe if(input.empty()) return default_value; + // Konvertiere Eingabe try { return std::stoul(input); } catch(...) { @@ -45,6 +58,7 @@ int main() { std::cout << "=== Sensornetzwerk-Simulation ===\n" << "(Leere Eingabe verwendet Standardwerte)\n"; + // Interaktive Konfiguration size_t num_sensors = get_input("Anzahl Sensoren", DEFAULT_NUM_SENSORS); size_t num_analysers = get_input("Anzahl Analysemodule", DEFAULT_NUM_ANALYSERS); int run_time = static_cast( @@ -52,6 +66,7 @@ int main() { ); size_t buffer_size = get_input("Puffergröße", DEFAULT_BUFFER_SIZE); + // Starte Simulation basierend auf Puffergröße switch(buffer_size) { case 8: run_simulation<8>(num_sensors, num_analysers, run_time); @@ -72,4 +87,4 @@ int main() { std::cout << "Simulation erfolgreich abgeschlossen.\n"; return 0; -} +} \ No newline at end of file diff --git a/ring_buffer.h b/ring_buffer.h index fc94a85..ee992c8 100644 --- a/ring_buffer.h +++ b/ring_buffer.h @@ -4,19 +4,30 @@ #include #include +/** + * Thread-sicherer Ringpuffer mit fester Größe + * @tparam N Größe des Puffers (muss > 1 sein) + * + * Implementiert das Producer-Consumer Pattern mit: + * - Mutex für exklusiven Zugriff + * - Condition Variable für blockierendes Pop + * - Überschreibt älteste Daten bei vollem Puffer + */ template class RingBuffer { static_assert(N > 1, "Buffer size must be greater than 1"); private: - std::vector data; - size_t read_ptr = 0; - size_t write_ptr = 0; - bool full = false; + std::vector data; // Speicher für Elemente + size_t read_ptr = 0; // Lesezeiger (nächstes zu lesendes Element) + size_t write_ptr = 0; // Schreibzeiger (nächstes freie Position) + bool full = false; // Flag, ob Puffer voll ist - std::mutex mtx; - std::condition_variable not_empty; + // Synchronisationsprimitive + std::mutex mtx; // Schützt alle internen Zustände + std::condition_variable not_empty; // Signalisiert, dass Daten verfügbar sind + // Hilfsfunktion: Zeiger mit Ringverhalten bewegen size_t advance(size_t ptr) const { return (ptr + 1) % N; } @@ -24,28 +35,56 @@ private: public: RingBuffer() : data(N, 0) {} + /** + * Schreibt Wert in den Puffer + * @param value Der zu schreibende Wert + * + * Funktionsweise: + * 1. Sperrt Mutex für exklusiven Zugriff + * 2. Schreibt Wert an aktueller write_ptr + * 3. Bei vollem Puffer: Bewegt read_ptr (überschreibt ältesten Wert) + * 4. Aktualisiert write_ptr und full-Flag + * 5. Benachrichtigt einen wartenden Consumer + */ void push(int value) { std::unique_lock lock(mtx); + // Schreibe Wert data[write_ptr] = value; + // Überschreibe ältesten Wert bei vollem Puffer if(full) { read_ptr = advance(read_ptr); } + // Zeiger aktualisieren write_ptr = advance(write_ptr); full = (write_ptr == read_ptr); + // Benachrichtige einen wartenden Consumer not_empty.notify_one(); } + /** + * Liest Wert aus dem Puffer (blockierend) + * @return Der gelesene Wert + * + * Funktionsweise: + * 1. Sperrt Mutex + * 2. Wartet mit Condition Variable bis Daten verfügbar + * 3. Liest Wert an read_ptr + * 4. Aktualisiert read_ptr und full-Flag + * 5. Gibt Wert zurück + */ int pop() { std::unique_lock lock(mtx); + // Warte bis Daten verfügbar (verhindert Busy Waiting) not_empty.wait(lock, [this]() { return !is_empty(); }); + // Lese und aktualisiere Zustand int value = data[read_ptr]; read_ptr = advance(read_ptr); full = false; @@ -53,11 +92,13 @@ public: return value; } + // Prüft ob Puffer leer ist bool is_empty() const { return !full && (read_ptr == write_ptr); } + // Prüft ob Puffer voll ist bool is_full() const { return full; } -}; +}; \ No newline at end of file diff --git a/sensor_network b/sensor_network index e01a289..a918fbc 100755 Binary files a/sensor_network and b/sensor_network differ diff --git a/sensor_network.cpp b/sensor_network.cpp index 65b5247..038a7ca 100644 --- a/sensor_network.cpp +++ b/sensor_network.cpp @@ -3,31 +3,43 @@ #include #include +/** + * Startet alle Threads des Netzwerks + * @param num_sensors Anzahl der Sensor-Threads + * @param num_analysers Anzahl der Analyse-Threads + */ template void SensorNetwork::start(size_t num_sensors, size_t num_analysers) { running = true; + // Starte Sensor-Threads for(size_t i = 0; i < num_sensors; ++i) { sensors.emplace_back([this, i] { sensor_thread(i); }); } + // Starte Analyse-Threads for(size_t i = 0; i < num_analysers; ++i) { analysers.emplace_back([this, i] { analyser_thread(i); }); } + // Starte Controller-Thread controller = std::thread([this] { controller_thread(); }); } +/** + * Stoppt alle Threads und wartet auf Beendigung + */ template void SensorNetwork::stop() { running = false; + // Warte auf Thread-Ende for(auto& t : sensors) { if (t.joinable()) t.join(); } @@ -39,18 +51,29 @@ void SensorNetwork::stop() { } } +/** + * Thread-Funktion für Sensoren (Producer) + * @param id Eindeutige ID des Sensors + * + * Funktionsweise: + * 1. Generiert zufällige Messwerte + * 2. Wartet zufällige Zeit (Messintervall) + * 3. Schreibt Daten in Ringpuffer + */ template void SensorNetwork::sensor_thread(int id) { std::random_device rd; std::mt19937 gen(rd()); - std::uniform_int_distribution<> data_gen(0, 100); - std::uniform_int_distribution<> sleep_gen(100, 500); + std::uniform_int_distribution<> data_gen(0, 100); // Messwerte 0-100 + std::uniform_int_distribution<> sleep_gen(100, 500); // Intervall 100-500ms while(running) { + // Simuliere Messintervall std::this_thread::sleep_for( std::chrono::milliseconds(sleep_gen(gen)) ); + // Generiere und schreibe Messwert int value = data_gen(gen); buffer.push(value); @@ -58,11 +81,22 @@ void SensorNetwork::sensor_thread(int id) { } } +/** + * Thread-Funktion für Analyse-Module (Consumer) + * @param id Eindeutige ID des Moduls + * + * Funktionsweise: + * 1. Liest Daten aus Ringpuffer (blockierend) + * 2. Liest aktuelles Analysemodell + * 3. Verarbeitet Daten (hier nur Ausgabe) + */ template void SensorNetwork::analyser_thread(int id) { while(running) { + // Blockierendes Lesen aus Puffer int data = buffer.pop(); + // Lesender Zugriff auf Analysemodell int model_value = model.read(); std::cout << "Analyser " << id << " processed: " << data @@ -70,18 +104,27 @@ void SensorNetwork::analyser_thread(int id) { } } +/** + * Thread-Funktion für System-Controller (Writer) + * + * Funktionsweise: + * 1. Wartet zufällige Zeit zwischen Updates + * 2. Schreibt neuen Wert ins Analysemodell + */ template void SensorNetwork::controller_thread() { std::random_device rd; std::mt19937 gen(rd()); - std::uniform_int_distribution<> update_gen(0, 100); - std::uniform_int_distribution<> sleep_gen(500, 2000); + std::uniform_int_distribution<> update_gen(0, 100); // Modellwerte + std::uniform_int_distribution<> sleep_gen(500, 2000); // Update-Intervall while(running) { + // Warte bis zum nächsten Update std::this_thread::sleep_for( std::chrono::milliseconds(sleep_gen(gen)) ); + // Aktualisiere Analysemodell int new_value = update_gen(gen); model.write(new_value); @@ -89,6 +132,7 @@ void SensorNetwork::controller_thread() { } } +// Explizite Instanziierung für gängige Puffergrößen template class SensorNetwork<8>; template class SensorNetwork<16>; -template class SensorNetwork<32>; +template class SensorNetwork<32>; \ No newline at end of file diff --git a/sensor_network.h b/sensor_network.h index 468af5f..1480dc3 100644 --- a/sensor_network.h +++ b/sensor_network.h @@ -5,12 +5,22 @@ #include "ring_buffer.h" #include "analysis_model.h" +/** + * Hauptklasse des Sensornetzwerks + * @tparam N Größe des Ringpuffers + * + * Verwaltet alle Komponenten: + * - Ringpuffer für Sensordaten + * - Analysemodell + * - Threads für Sensoren, Analyse und Controller + */ template class SensorNetwork { - RingBuffer buffer; - AnalysisModel model; - std::atomic running{false}; + RingBuffer buffer; // Gemeinsamer Datenpuffer + AnalysisModel model; // Geteiltes Analysemodell + std::atomic running{false}; // Steuerflag für Threads + // Thread-Container std::vector sensors; std::vector analysers; std::thread controller; @@ -24,7 +34,8 @@ public: void stop(); private: + // Thread-Funktionen void sensor_thread(int id); void analyser_thread(int id); void controller_thread(); -}; +}; \ No newline at end of file