No description
| .gitignore | ||
| analysis_model.h | ||
| CMakeLists.txt | ||
| main.cpp | ||
| README.md | ||
| ring_buffer.h | ||
| sensor_network | ||
| sensor_network.cpp | ||
| sensor_network.h | ||
Erklärung der Praktikumsaufgabe (Synchronisation)
Kernziel der Aufgabe
Sie sollen ein multithreaded Sensornetzwerk simulieren, das drei Komponenten umfasst:
- Sensoren (Producer)
- Erzeugen regelmäßig Messdaten (z.B. Temperatur) als Zufallszahlen.
- Speichern Daten in einem gemeinsamen Ringpuffer (Thread-sicher synchronisiert).
- Analyse-Module (Consumer & Reader)
- Entnehmen Daten aus dem Ringpuffer.
- Lesen gleichzeitig ein zentrales Analysemodell (z.B. Kalibrierungsdaten).
- System-Controller (Writer)
- Aktualisiert das Analysemodell in zufälligen Abständen.
Technische Anforderungen
- Synchronisationsmechanismen
- Nutzen Sie nur Semaphore und Mutex (wie in der Vorlesung behandelt).
- Vermeiden Sie Race Conditions und Deadlocks.
- Ringpuffer
- Vorgegebene Implementierung (nicht threadsicher!) → Sie müssen Zugriff synchronisieren.
- Eigenschaften:
- Größe
BUFFER_SIZE(z.B. 8 Elemente). - Überschreibt älteste Daten bei Vollschreiben (
ring_push). - Blockiert beim Lesen, wenn leer (
ring_pop).
- Größe
- Analysemodell
- Einfache Implementierung als globaler Integer (keine komplexe Analyse).
- Reader-Writer-Problem:
- Mehrere Leser (Analyse-Module) dürfen gleichzeitig lesen.
- Writer (Controller) benötigt exklusiven Zugriff (kein Leser/Writer aktiv).
- Starvation des Writers verhindern (Controller darf nicht endlos warten).
- Simulation
- Sensoren/Controller:
sleep()+rand()für Intervalle. - Ausgaben: Nutzen Sie
printf()zur Beobachtung des Systems.
- Sensoren/Controller:
Zu implementierende Synchronisation
1. Ringpuffer (Producer-Consumer Pattern)
-
Problem: Paralleler Zugriff von Sensoren (Produzenten) und Analyse-Modulen (Konsumenten).
-
Lösung:
- Mutex: Schützt den Puffer bei
push/pop(strukturelle Integrität). - Semaphore
items: Zählt belegte Pufferplätze.- Initialwert:
0(leer). - Producer (Sensor): Erhöht
itemsnur wenn Puffer vorher nicht voll war (sonst Überschreiben ohne Signal). - Consumer (Analyse-Modul): Wartet auf
items > 0vor dem Lesen.
- Initialwert:
// Pseudocode: Sensor (Producer) pthread_mutex_lock(&buffer_mutex); bool was_full = ring_is_full(&rb); ring_push(&rb, new_data); if (!was_full) sem_post(&items); // Signal an Consumer pthread_mutex_unlock(&buffer_mutex); // Pseudocode: Analyse-Modul (Consumer) sem_wait(&items); // Wartet auf Daten pthread_mutex_lock(&buffer_mutex); ring_pop(&rb, &data); pthread_mutex_unlock(&buffer_mutex); - Mutex: Schützt den Puffer bei
2. Analysemodell (Reader-Writer Problem mit Fairness)
-
Problem: Paralleles Lesen vs. exklusives Schreiben + Verhinderung von Writer-Starvation.
-
Lösung (Fairness mit "Turnstile"-Semaphor):
- Semaphore
turn: Garantiert FIFO-Reihenfolge (Fairness). - Semaphore
rw_mutex: Schützt exklusive Schreibzugriffe. - Mutex
read_mutex: Schützt Leser-Zähler (read_count). - Integer
read_count: Zählt aktive Leser.
// Pseudocode: Analyse-Modul (Reader) sem_wait(&turn); // Stelle dich in Warteschlange sem_post(&turn); // Weiterleitung (sofort) pthread_mutex_lock(&read_mutex); read_count++; if (read_count == 1) sem_wait(&rw_mutex); // Erster Leser sperrt Writer pthread_mutex_unlock(&read_mutex); // LESEZUGRIFF auf shared_model pthread_mutex_lock(&read_mutex); read_count--; if (read_count == 0) sem_post(&rw_mutex); // Letzter Leser erlaubt Writer pthread_mutex_unlock(&read_mutex); // Pseudocode: Controller (Writer) sem_wait(&turn); // Warte auf deine Reihenfolge sem_wait(&rw_mutex); // Fordere exklusiven Zugriff an sem_post(&turn); // Freigabe für Nächsten in Warteschlange // SCHREIBZUGRIFF auf shared_model sem_post(&rw_mutex); // Freigabe - Semaphore
Test und Reflexion
Starten Sie das System mit verschiedenen Parametern und beobachten Sie:
- Durchsatzrate
- Wie viele Daten werden pro Zeiteinheit verarbeitet?
- Variieren Sie: Puffergröße, Anzahl der Sensoren/Analyse-Module.
- Starvation/Deadlocks
- Tritt Starvation auf (z.B. Controller wird blockiert)?
- Verursachen bestimmte Konfigurationen Deadlocks?
- Lastszenarien
- Viele Sensoren: Läuft der Puffer über? (Datenverlust durch Überschreiben)
- Viele Leser: Wird der Controller blockiert? Wie wirkt sich Fairness aus?
- Systemunterbrechungen
- Simulieren Sie OTA-Updates: Controller führt
sleep()während Schreibzugriff aus → Blockiert Leser?
- Simulieren Sie OTA-Updates: Controller führt
- Optimale Parameter
- Finden Sie Konfigurationen, bei denen das System besonders effizient ist.
Umsetzungshinweise
-
Globale Variablen:
RingBuffer rb; // Ringpuffer int shared_model = 0; // Analysemodell (Integer) sem_t items, rw_mutex, turn; // Semaphore pthread_mutex_t buffer_mutex, read_mutex; // Mutexe int read_count = 0; // Zähler aktiver Leser -
Thread-Erstellung:
// Beispiel: Starte 2 Sensoren, 3 Analyse-Module, 1 Controller pthread_t sensor_threads[2], analyser_threads[3], controller_thread; for (int i = 0; i < 2; i++) pthread_create(&sensor_threads[i], NULL, sensor_func, NULL); for (int i = 0; i < 3; i++) pthread_create(&analyser_threads[i], NULL, analyser_func, NULL); pthread_create(&controller_thread, NULL, controller_func, NULL); -
Wichtig: Initialisieren Sie alle Synchronisationsmittel vor Thread-Start!
sem_init(&items, 0, 0); // Initialwert 0 sem_init(&rw_mutex, 0, 1); // Initialwert 1 (frei) sem_init(&turn, 0, 1); // Initialwert 1 (FIFO-Reihenfolge) pthread_mutex_init(&buffer_mutex, NULL); pthread_mutex_init(&read_mutex, NULL);
Warum diese Lösung?
- Ringpuffer: Produzenten überschreiben alte Daten, Konsumenten warten bei Leerpuffer → Effizient & verlusttolerant.
- Analysemodell:
turn-Semaphor verhindert Writer-Starvation (Fairness).- Leser können parallel arbeiten, solange kein Writer aktiv ist.
- Synchronisationsmittel: Beschränkt auf Semaphore/Mutex (vorgabekonform).
Mit dieser Struktur erfüllen Sie alle Lernziele:
✅ Reader-Writer-Problem
✅ Producer-Consumer-Pattern
✅ Vermeidung von Race Conditions & Deadlocks!