Der Hamster kann mehrere Skripte nebeneinander ausführen. Jedes Skript läuft dabei völlig unabhängig von allen anderen, unabhängig auch von den übrigen Vorgängen im Hamster. Der Hamster sorgt mit Hilfe des Betriebssystems dafür, dass es beim Zugriff auf Netzwerkverbindungen, Dateien und anderen kontingentierten Ressourcen nicht zu Kollisionen kommt. Einen solcher Ablauf ist ein Prozess, jedes laufende Skript bildet zusammen mit seinen globalen Variablen einen Hamsterprozess. Selbst wenn zweimal das gleiche Skript gestartet wird, beginnen zwei verschiedene Hamsterprozesse mit eigenen Daten in voneinander unabhängig gespeicherten globalen Variablen.
Auf einem Rechner mit einem Prozessor oder unter Windows 9x und Windows ME würde jedes Skript für kurze Zeit fortgesetzt, um dann zugunsten eines anderen auszusetzen, so dass der Prozessor jedes Skript und die übrigen Vorgänge im Hamster immer nur für kurze Zeit vorantreibt. Dieser Wechsel wird jedoch schnell genug vollzogen, so dass es für den menschlichen Nutzer wie ein gleichzeitiger Ablauf aussieht.
Nur auf einem Rechner mit mehreren Prozessoren und unter Windows NT bzw. ab Windows 2000 können Prozesse auch real gleichzeitig ablaufen. In der Regel hat allerdings auch hier das Betriebssystem eine Anzahl von Prozessen gestartet, so dass die Rechenzeit selbst in einem Mehrprozessorsystem nur alternierend zugeteilt wird.
Die Hamsterprozesse laufen letztlich als Windows-Threads, die zum Windows-Prozess des Hamsters gehören. Wenn der Hamster genauer: sein Windows-Prozess beendet wird (gleich aus welchem Grund), enden alle seine Threads und damit auch alle Hamsterprozesse. Im Folgenden bezeichnet der Begriff des Prozesses immer einen Hamsterprozess, also die Ausführung eines Skriptes oder eines anderen Vorganges im Hamster.
Vor der Synchronisation steht oft der skriptgesteuerte Start anderer Skripte. Es macht beim Hamster allerdings keinen Unterschied, ob ein Skript per Kommandozeilenoption, das Skriptmenü oder skriptgesteuert gestartet wird.
Die Prozedur startet das Skript, dessen Namen im ersten Parameter <scriptname> ohne Pfad, aber mit Extension .hsc anzugeben ist; es wird im Skripteverzeichnis des Hamsters gesucht. Diesem Skript wird im zweiten Parameter <strParameter> ein String übergeben, der die Kommandozeilenparameter für dieses Skript enthält.
Der Boolesche Parameter <boolWait> steuert die Nebenläufigkeit: Wird darin 0 übergeben, kehrt die Prozedur sofort nach dem Start des aufgerufenen Skriptes zurück, so dass aufgerufenes und aufrufendes Skript nun nebeneinander ausgeführt werden. Bei jedem anderen Wert kehrt die Prozedur erst zurück, wenn das aufgerufene Skript beendet wurde; das aufrufende Skript wird erst nach Beendigung des aufgerufenen Skriptes fortgesetzt.
Abgesehen von einer gewissen Zeitersparnis wäre die Möglichkeit der Nebenläufigkeit ohne Synchronisation kaum interessant: Man könnte nur solche Skripte nebenläufig ausführen lassen, deren Tätigkeit in keinerlei Beziehung zueinander steht. Die folgenden Prozeduren bzw. Funktionen ermöglichen in häufig anzutreffenden Spezialfällen eine recht einfache Synchronisation:
HamWaitIdle kehrt erst zurück, wenn der Hamster keine Jobs mehr ausführt oder <intTimeout> ms (= 1/1000 Sekunden) vergangen sind. Ohne diesen Parameter oder mit dem Wert -1 wird unbegrenzt auf die Abarbeitung aller laufenden Jobs gewartet.
Die Funktion gibt den Wert 0 zurück, wenn keine Jobs mehr laufen; den Wert 1, falls die Timeout-Zeit abgelaufen ist und den Wert 2, falls die laufenden Jobs abgebrochen wurden.
Die Prozedur kehrt zurück, wenn <intTime> ms (= 1/1000 Sekunden) vergangen sind.
Die im vorhergehenden Abschnitt erläuterten Aufrufe halten das aufrufende Skript an, bis der Hamster einen bestimmten Zustand einnimmt (HamWaitIdle(): Hamster führt keine Jobs aus) oder ein Ereignis eintritt (sleep(): Ablauf einer Zeitspanne). Generell bedeutet Synchronisation, den Ablauf von Prozessen zum Zwecke der Koordination vom Verhalten anderer Prozesse abhängig zu machen. Sie ist immer dann erforderlich, wenn Kollisionsfreiheit allein nicht ausreicht.
Windows und der Hamster garantieren beispielsweise, dass eine Datei zu jedem Zeitpunkt nur durch einen einzigen Prozess bearbeitet wird. Damit ist aber noch nicht sichergestellt, dass ein schreibender Prozess grundsätzlich vor einem lesenden Prozess zugreift, damit letzterer überhaupt Daten zur Verarbeitung vorfindet. Dazu müssten Windows und der Hamster die Skripte lesen und verstehen, die diese Prozesse beschreiben. Weil das nicht möglich ist, fügt man in die Skripte Anweisungen ein, die es bis zum Eintritt eines Ereignisses anhalten (warten lassen) oder seine Fortsetzung von einem Zustand abhängig machen.
Seit der Version Hamster Classic 1.3.22.100 beta können Skripte auf beliebige Ereignisse (Events) warten, die von nebenläufigen Skripten oder bestimmten Vorgängen im Hamster ausgelöst bzw. als Zustand gesetzt werden. Sie werden zur Unterscheidung mit Namen versehen.
Weil auf einem Rechner mehrere Hamster arbeiten können und Ereignisnamen systemweit eindeutig sein müssen, ist dem Namen im Wege der Stringconkatenation eine Prozessidentifikation (Mutex-String) voranzustellen, die mit der eingebauten Funktion GetProcessIdentifier abgefragt werden kann (siehe auch die Erläuterungen zu den Hamster-Mutex-Objekten). Die Funktion
kehrt zurück, wenn das Ereignis mit dem Namen <strEventname> eingetreten (gesetzt) ist oder die Zeit von <intTimeout> ms (= 1/1000 Sekunden) abgelaufen ist. Ohne den Parameter <intTimeout> oder mit dem Wert -1 wird unbegrenzt auf das Ereignis gewartet. Wenn und solange das Ereignis <strEventname> gesetzt ist, kehrt diese Prozedur bei jedem Aufruf sofort zurück, auch wenn zuvor schon Wartezeiten darauf abgelaufen sind.
Wenn das Ereignis unter dem Namen <strEventname> nicht gefunden werden konnte, gibt die Funktion dann einen Wert größer 1 als Fehlercode zurück, sonst den Wert 0 oder 1. Ein Rückgabewert von 1 bedeutet, dass der Hamster extern beendet werden soll und die Funktion daher zurückkehrt. Der Fehlercode 258 zeigt den Ablauf der maximalen Wartezeit <intTimeout> an.
Diese Funktion wird also genau wie HamWaitIdle() gebraucht, nur dass auf ein beliebiges, durch seinen Namen gekennzeichnetes und im ersten Parameter zu übergebendes Ereignis gewartet wird.
Die folgenden beiden Funktionen dienen der Signalisierung von Zuständen:
löst das Ereignis mit dem Namen <strEventname> aus. Das Ereignis bleibt dem Umlegen eines Schalters vergleichbar dauernd ausgelöst, bis es mit EventReset()oder automatisch nach Vorgabe in der Funktion EventCreate() zurückgesetzt wird. Daher kennzeichnet ein derartig ausgelöstes Ereignis auch einen Zustand. Wenn das Ereignis unter dem Namen <strEventname> nicht gefunden werden konnte, gibt die Funktion den Wert -1 zurück, sonst den Wert 0.
Die Funktion setzt das Ereignis mit dem Namen <strEventname> zurück.
Wenn das Ereignis unter dem Namen <strEventname> nicht gefunden werden konnte, gibt die Funktion den Wert -1 zurück, sonst den Wert 0.
Nachdem ein Ereignis durch EventSet() ausgelöst wurde, kehren auch alle späteren auf dieses Ereignis wartenden EventWait()-Aufrufe sofort zurück, bis ein EventReset()-Aufruf erfolgt.
Die Funktion löst das Ereignis mit dem Namen <strEventname> aus. Das Ereignis wird danach sofort wieder zurückgesetzt; das Verhalten der damit ausgelösten Ereignisse entspricht einem Taster, der nach Betätigung wieder in seine Ausgangsposition zurückfedert.
Wenn das Ereignis unter dem Namen <strEventname> nicht gefunden werden konnte, gibt die Funktion den Wert -1 zurück, sonst den Wert 0.
Bei einem mit EventPulse() ausgelösten Ereignis kehren lediglich alle vor der Auslösung erfolgten EventWait()-Aufrufe für dieses Ereignis zurück. Spätere Aufrufe warten bis zur nächsten Auslösung.
Der Hamster kennt folgende Ereignisse (Ereignisnamen ohne Mutex-String aus GetProcessIdentifier):
| Ereignisname | auslösender Vorgang | Auslösung durch |
| evtHamster.IsIdle | Keine Prozesse im Hamster aktiv und Hamster unbeschäftigt (idle), siehe auch Hinweis. | EventSet() |
| evtHamster.StopScript | Abbruch der Skriptbearbeitung gefordert. | EventSet() |
| _main | Hamster läuft. | EventSet() |
| _mailin | E-Mail-Empfang vom Remote-Server empfangen. | EventPulse() |
| _mailout | E-Mail-Empfang vom lokalen Server mit externem Ziel. | EventPulse() |
| _mailinternal | E-Mail-Empfang vom lokalen Server mit internem Ziel. | EventPulse() |
| _newsout | Artikel-Empfang vom lokalen Server. | EventPulse() |
| _purge | Der Hamster putzt sich. | EventSet() |
| _rasconnected | Aufbau einer DFÜ-Verbindung. | EventSet() |
| _rashangup | Trennung einer DFÜ-Verbindung. | EventPulse() |
Hinweis: Verbindungen zu den lokalen Servern werden nicht als aktiver Auftrag betrachtet.
Skripte können aber auch beliebig viele neue Ereignisse definieren. Sie sind mit der Funktion EventCreate() zu deklarieren. Dabei wird ein Name für sie festgelegt, unter dem sie ausgelöst werden können bzw. unter dem auf sie gewartet werden kann.
Die Funktion deklariert ein neues Ereignis unter dem Namen <strEventname>. Wird im Parameter <boolReset> 0 übergeben, wird das Ereignis nach der Abfrage nicht automatisch zurückgesetzt, Standardwert ist 1. Wird in <boolInitial> der Wert 1 übergeben, wird das Ereignis sofort ausgelöst. Die Funktion gibt eine Zahl (Eventhandle) zurück, die zum Löschen des Ereignisses mit EventClose() benötigt wird:
Die Prozedur löscht eine Ereignisdeklaration.
Deklaration und Löschung müssen zumindest im ereignisauslösenden Skript erfolgen. Sie können zusätzlich auch im reagierenden Skript vorgenommen werden, um die Existenz eines Ereignisses sicherzustellen.
wartet auf eines von mehreren Events. Wird eines von mehreren Events aktiv oder tritt das <Timeout> in ms (= 1/1000 Sekunden) auf, liefert diese Funktion folgende Ergebnisse:
| Ergebnis | Bedeutung |
| 1...10 | Eintritt des jeweiligen Eventparameters |
| 0 | Stop-Script-Ereignis aufgetreten |
| -1 | Timeout |
| -2 | Fehler |
Das folgende Beispiel realisiert eine Anwendung, die automatisch alle Mailaccounts abfragt und evtl. ausgehende Mails versendet, sobald eine DFÜ-Verbindung hergestellt wird. Sie besteht aus einem Skript Einwahl.hsc, das die DFÜ-Verbindung herstellt und den Erfolg durch die Auslösung eines Ereignisses signalisiert. Ein weiteres Skript Mailexchange.hsc wartet auf dieses Ereignis und führt den Mailexchange durch, sobald es eintritt.
Die beiden folgenden Beispielskripte sind in je einer *.hsc-Textdatei im Skripteverzeichnis des Hamsters unterzubringen. Beide Skriptnamen sollten dann nach dem nächsten Start des Hamsters oder bei laufendem Hamster nach Selektion des Menüs Datei → Aktualisiere Menüeinträge im Skriptmenü erscheinen.
Das Skript Einwahl.hsc lautet wie folgt:
#!hs2
# Einwahl.hsc
# Stellt eine DFÜ-Verbindung her
var($try, $err)
varset($Tries, 3) # Anzahl der Einwahlversuche
varset($Connection, "RAS-Provider" ) #DFÜ-Verbindung
# 1.) Ereignis deklarieren ...
# ... Prozess- und Ereignisnamen bestimmen
varset($Proc, GetProcessIdentifier)
varset($Event, $Proc + "_rasconnected" + "_" + $Connection)
# ... Ereignis erzeugen
varset$hdlEvent, EventCreate($Event, 0, 0, $err))
# 2.) Einwahlversuche
for($try, 1, $Tries)
# 2.a) Einwahlversuch
$err = HamRasDial($Connection)
# 2.b) DFÜ-Verbindung brauchbar?
if($err = 0)# Kein DFÜ-Fehler aufgetreten?
if(LocalhostAddr = 0) # Keine IP-Adresse zugeteilt?
HamRasHangup # -> Auflegen, DFÜ-Verbindung ohne IP-Adresse unbrauchbar
else
EventPulse( $Event ) # -> Ereignis auslösen, da Einwahl # erfolgreich
break # Einwahlschleife verlassen
endif
endif
endfor
# 3.) Ereignis löschen
EventClose($hdlEvent)
quit
Das Skript deklariert im Abschnitt 1 ein Ereignis mit dem Namen $Event,
das die Herstellung der DFÜ-Verbindung des Namens $Connection
meldet. Im Abschnitt 2 wird die Einwahl versucht (2.a) und falls
das DFÜ-Netzwerk keine Fehler meldet die Brauchbarkeit der
DFÜ-Verbindung überprüft (2.b). Eine DFÜ-Verbindung
ist brauchbar, wenn eine IP-Adresse zugeteilt wurde. Der Abschnitt 2
befindet sich in einer
For-Schleife, damit bei
fehlschlagender Einwahl mehrere Versuche unternommen werden.
Das Skript Mailexchange.hsc lautet wie folgt:
#!hs2
# Mailexchange.hsc
# Fragt POP3-Server ab und veranlasst den Versand ausgehender Mails
var($err)
# A.) Ereignis deklarieren ...
# ... Prozess- und Ereignisnamen bestimmen
varset($Proc, GetProcessIdentifier)
varset($Connection, "_RAS-Provider")
# (der Unterstrich _ musste hier ergänzt werden, da er nicht
# in das folgende varset eingefügt werden soll wegen der
# Ausführungen unten im Text)
varset($Event, $Proc + "_rasconnected" + $Connection)
# ... Ereignis erzeugen
varset($hdlEvent, EventCreate($Event, 0, 0, $err))
# B.) Ereignisschleife
do
# B.1) Ereignis erwarten
EventWait($Event, -1)
# B.2) Mailexchange durchführen ...
# ... Abfrage
HamFetchMail("pop3-server.one.example.com","pop3", _
"$1", "", "lokaler_Nutzer")
# ... Abfrage des lokalen Administrator-Accounts
# (wäre auch ohne DFÜ-Verbindung möglich)
HamFetchMail("localhost", "pop3", _
"$99", "", "lokaler_Nutzer")
# ... Versand per SMTP-AUTH
HamSendMailAuth("smtp-server.one.example.com", "smtp", _
"$1", "", "user@one.example.com")
loop
# C.) Ereignis löschen
EventClose($hdlEvent)
quit
Im Abschnitt A wird das Ereignis deklariert, auf das hier mit dem Mailaustausch reagiert werden soll.
Der Abschnitt B besteht aus der Ereignisschleife, die endlos also bis zur Beendigung des Hamsters läuft. Bei B.1 kehrt die Funktion EventWait() erst zurück, wenn das deklarierte Ereignis eingetreten ist; bis dahin wartet Mailexchange.hsc.
Das erwartete Ereignis kann erst eintreten, wenn der Nutzer im Skriptmenü auch den Eintrag Einwahl aufruft und damit das Skript Einwahl.hsc startet. Dieses versucht, eine DFÜ-Verbindung herzustellen und löst das Ereignis im Falle erfolgreicher Einwahl aus. Gelingt die Einwahl, erwacht auch das wartende Skript Mailexchange.hsc, weil jetzt dessen EventWait()-Aufruf unter B.1 zurückkehrt. Nun wird dort der Mailexchange durch die Anweisungen unter B.2 ausgeführt. Mailexchange.hsc muss vorher gestartet worden sein; ideal wäre, dies beim Start des Hamsters durch eine Action oder als Kommandozeilenoption zu erledigen.
Da Einwahl.hsc das Ereignis durch EventPulse() auslöst, wird es im Skript Mailexchange.hsc nach der Rückkehr des EventWait()-Aufrufes sofort wieder zurückgesetzt. Wenn die Ereignisschleife im Abschnitt B also nach Ausführung der Anweisungen unter B.2 erneut EventWait() aufruft, wartet es bis zur erneuten Herstellung der DFÜ-Verbindung. Anderenfalls würden die Anweisungen unter B.2 fortwährend wiederholt.
Im Skript Mailexchange.hsc kann die Zeile varset($Connection, "_Nexgo") auch durch die Zeile varset(Connection, "") ersetzt werden; das Skript würde damit das durch den Ausdruck GetProcessIdentifier + "_rasconnected" gegebene hamstereigene DFÜ-Einwahlereignis erwarten. Doch das durch Einwahl.hsc deklarierte Einwahlereignis hat den Vorzug, die DFÜ-Verbindung nicht nur herzustellen, sondern auch auf Zuteilung einer IP-Adresse zu überprüfen.
Die beiden Skripte dieses einfach gehaltenen Beispiels kommunizieren praktisch interprozessual durch das mit EventCreate($Event, 0, 0, $err) deklarierte Ereignis. Dazu müssen beide Skripte vom gleichen Ereignisnamen ausgehen, also gemeinsame Informationen nutzen. In größeren Projekten empfiehlt es sich daher, die Ereignisnamen durch globale Variablen eines Moduls bereitzustellen. In dessen Initialisierungsteil veranlasst man auch gleich die erforderlichen EventCreate()-Aufrufe. Damit sind die selbstdefinierten Ereignisse bereits durch einfaches Laden dieses Moduls gebrauchsfertig und es ist sichergestellt, dass alle beteiligten Skripte auch nach Namensänderungen die richtigen Bezeichnungen verwenden. Das Modul sollte auch eine Prozedur bieten, die alle selbstdefinierten Ereignisse mit EventClose() wieder löscht (Nutzung eines Finalisierungsteils).
Mehr zu diesem Thema siehe auch im FAQ-Beitrag Hamster und Events und für ausführliche Beispiele Sofortiges Versenden Von E-Mails.
(Für die Nutzung von Events mit anderen Skriptsprachen siehe Hamster-Event-Objekte.)