Mit Semaphoren kann der Zugriff auf Ressourcen kontrolliert werden. Sie werden in Multithreaded-Umgebungen verwendet, um Zugriff auf gemeinsame Ressourcen, zum Beispiel Speicherplatz oder gemeinsam genutzte Dateien, zu kontrollieren.
Mutex ist eine Klasse, mit der man einfach Semaphore für jeweils exklusiven Zugriff schreiben kann. Dabei wird ein bestimmtes Objekt für den Zugriff durch andere Threads gesperrt; diese können entweder auf die Freigabe warten oder einen Zugriffsfehler melden. An dieser Stelle muss besonders vorsichtig gearbeitet werden, da sich so genannte "`Deadlocks"' schnell einschleichen, bei denen sich zwei Threads gegenseitig blockieren.
Mutex-Instanzen werden oft verwendet, um Änderungen an gemeinsam genutzten Daten ungestört abwickeln zu können. Inkonsistenzen wegen "`halbaktueller"' Daten können nicht auftreten, da die Aktualisierung effektiv atomar abläuft.
Ein kleines Beispiel: Ein Thread erhöht für 3 Sekunden einen Zähler, auf den ein zweiter gleichzeitig zugreift. Ohne Sperrmechanismus gibt es viele Reste, da die Synchronisation der beiden unabhängigen Schleifen nicht möglich ist.
zahl = rest = 0
zaehler = Thread.new do
loop do
13.times { zahl += 1}
end
end
ueberwacher = Thread.new do
loop do
rest += 1 if zahl % 13 != 0
end
end
sleep 3
printf "Zähler: %d, Reste: %d\n", zahl, rest
#-> Zähler: 1566605, Reste: 701217
Mit einem Mutex-Objekt kann der Zugriff besser kontrolliert werden.
Die Methode synchronize sperrt mutex, führt den
Codeblock aus und gibt mutex wieder
frei. Mittlerweile hat
aber der zweite Thread (der ueberwacher)
seinen Bedarf angemeldet, so dass sein synchronize-Block
abgearbeitet wird, bevor zaehler wieder weiterarbeiten kann.
Diese gegenseitigen "`Absprachen"' kosten auch etwas Rechenzeit.
require 'thread'
mutex = Mutex.new
zahl = rest = 0
zaehler = Thread.new do
loop do
mutex.synchronize do
13.times { zahl += 1 }
end
end
end
ueberwachen = Thread.new do
loop do
mutex.synchronize do
rest += 1 if zaehler % 13 !=0
end
end
end
sleep 3
Thread.critical = true
mutex.lock
printf "Zähler: %d, Reste: %d\n", zahl, rest
#-> Zähler: 1538407, Reste: 0