-
ALLGEMEINER
STAND DER TECHNIK
-
Diese
Erfindung betrifft Computerbetriebssysteme. Insbesondere betrifft
diese Erfindung Ereignisse und die Synchronisation der Ausführung eines
Programms mit jenen Ereignissen.
-
Ein
Verständnis
von gewissen Computerbetriebssystemkonzepten ist notwendig, um diese
Erfindung zu verstehen. UNIXTM wird als
ein Beispiel genommen. (UNIXTM wird umfassender
in der Portable perating System Interface, Part I (International
Standard ISO/IEC 9945-1: 1990 IEEE Std 1003.1-1990, 1990) ("Posix") beschrieben.)
-
Bei
UNIXTM ist ein "Prozess" oder "Steuerungs-Thread" eine dynamische Laufzeitausführungsform
eines Programms. Das Programm verharrt typischerweise in einem statischen
Zustand auf einem Speichermedium wie einer Disk oder in einem ROM,
während
der Prozess in den Speicher geladen und ausgeführt wird. UNIXTM ist
ein Multitasking-Betriebssystem: viele Prozesse und Threads können im
Wesentlichen gleichzeitig ausgeführt
werden.
-
Viele
Betriebssysteme auf diesem Gebiet der Technik und insbesondere alle
Spielarten von UNIXTM stellen einen Prozess
mit der Funktionalität
bereit, seine Ausführung
mit dem Auftreten eines bestimmten Ereignisses zu synchronisieren.
-
Solch
ein Ereignis könnte
den Abschluss eines synchronen I/O Betriebs, das asynchrone Eintreffen von
Daten von einem entfernten Clientgerät, das Entsperren eines Mutex,
den letzte Befehl eines Threads oder Unterprozesses, usw. umfassen.
Jedes Ereignis weist traditionell einen entsprechenden Benachrichtigungsmechanismus
auf, durch welchen ein Prozess das tatsächliche Auftreten des Ereignisses
erfassen kann. Zur zeit gibt es jedoch keinen anstrebens werten Mechanismus,
um es einem Prozess oder Thread, der auf solch einem Betriebssystem
läuft,
zu ermöglichen,
sich mit mehr als einer Ereignisbenachrichtung zum selben Zeitpunkt
zu synchronisieren.
-
Als
ein Beispiel des Problems könnte
ein Prozess oder Thread es wünschen,
die Ausführung
zu unterbrechen, bis entweder eine Ressource (z. B. ein bestimmter
Mutex) frei wird und dann verfügbar
gemacht werden kann oder bis eine Anfrage von einem Fernclientgerät empfangen
wurde, die anzeigt, dass ein I/O-Paket geprüft werden muss. Unter der Annahme,
dass jedes dieser Ereignisse als erstes auftreten kann, dass jedes
Auftreten den Prozess oder Thread aufwecken muss und dass der Prozess
oder Thread nur eines der Ereignisse synchronisieren kann, dann
sollte der Prozess oder Thread welches dieser Ereignisse blockieren?
-
Es
gibt mehrere Standardlösungsansätze, die
dieses allgemein auftretende Problem teilweise lösen: vollständige Betriebssystemunterstützung, Timeouts,
Signale, Auswahl, asynchrone I/O und Threads. Jede Teillösung beruht
darauf, welche Funktionalität
auch immer das Betriebssystem, das unter dem Anwenderprogramm liegt,
gerade bereitstellen kann. Jede Teillösung wird nacheinander im Folgenden
geprüft.
-
Vollständige Betriebssystemunterstützung
-
Wo
verfügbar,
ist dies oft die beste Wahl für
Anwendungsentwickler. Das Betriebssystem stellt die Funktionalität des Synchronisierens
eines Prozesses mit einem Satz von vielfachen, unabhängigen Ereignissen
bereit. Ein Beispiel von vollständiger
Betriebssystemunterstützung
ist die WaitForMultipleObjects()-Routine in der Win32-Anwendungsprogrammschnittstelle
(API), die unter dem NT®-Betriebssystem läuft, das
von der Microsoft Corporation, Redmond, Washington, erhältlich ist.
-
Jedoch
ist vollständige
Betriebssystemunterstützung
nicht allgemein in Betriebssystemen verfügbar und daher nicht allgemein
für Prozesse
verfügbar,
die wünschen,
sich mit vielfachen, unabhängigen
Ereignissen zu synchronisieren.
-
Timeouts
-
Viele
Betriebssysteme stellen eine Timeout-Funktionalität bereit,
wobei ein Prozess oder Thread unterbrochen (oder ruhig gestellt)
und dann nach einer vorher bestimmten Zeit wieder aufgenommen (oder
aufgeweckt) werden kann. Um mehrfache, unabhängige Ereignissynchronisation
bei gegebener Timeout-Funktionalität zu ermöglichen, kann ein Entwickler
Timeouts in einen Teildienst einbauen, der den Prozess oder Thread
alle n Zeiteinheiten aufweckt. Bei jedem Aufwachen prüft der Prozess
oder Thread, ob eine beliebige aus einem Satz von Ereignisbenachrichtigungen
aufgetreten ist. Er verfällt
zurück
in den Ruhezustand, wenn tatsächlich
keine aufgetreten ist.
-
Das
Problem mit einem Timeout-Teildienst besteht darin, dass er zyklisches
Abfragen für
eine asynchrone Unterbrechungsdienstleistung ersetzt. Dieser Ersatz
hat unangenehme Auswirkungen, die ihn in jeder Situation, wo entweder
schnelle Antwort oder Gesamtleistung ein Thema ist, nutzlos werden
lassen.
-
Wenn
die Schlafzeit zu lang ist, leidet die Programmantwort darunter.
Viele Ereignisse (I/O-Abschluss, Ankunft von Clientdaten, Verfügbarkeit
eines kritischen Bereichs) treten häufig auf und liegen entlang
der kritischen Leistungspfade der Programme, die sie erfassen. Der
Verzögerungsdienst
zum nächsten
Timeout hin führt
zu einer unannehmbaren Verschlechterung der Programmleistung.
-
Andererseits
leidet die Systemantwort, wenn die Ruhezustandszeit zu kurz ist.
Die allgemeinen Anforderungen des schnellen Aufweckens einer Reihe
von Prozessen oder Threads – nur
um sie Code ausführen zulassen,
um nach Ereignisbenachrichtigungen zu suchen, und sie dann wieder
in den Schlafzustand zurückzusetzen – führt zu einer
schweren und unannehmbaren Belastung der Systemressourcen.
-
Signale
-
Verschiedene
Betriebssysteme verwenden Signale, um über sowohl synchrone als auch
asynchrone Ereignisse zu berichten. In einem gegebenen Betriebssystem
sind die Arten der Signale sowohl vordefiniert als auch begrenzt.
Die Bandbreite der möglichen
Ereignisse und die Anzahl der gleichzeitig aktiven Prozesse und
Threads sind dies jedoch nicht. Die vordefinierte und begrenzte
Natur der Signale schließt
das Zuweisen eines einzigartigen Signals zu jedem Prozess oder Thread
aus, wobei das Signal ausgelöst
werden würde, wenn
eine der Ereignisbenachrichtigungen, auf die der Prozess oder Thread
gerade wartet, auftritt.
-
Es
bleibt die Technik des Setzens eines Signals, um alle Prozesse und
Threads aufzuwecken, wenn eine Ereignisbenachrichtigung auftritt.
Solch eine Umsetzung erzeugt die reale Möglichkeit von verlorenen Signalen,
die bewirken, dass ein Prozess völlig
herunterfährt.
Auch leiten die meisten Multithreading-Umsetzungen ein Signal an
nur einen Thread weiter, der deswegen blockiert hat. Dies macht
den letzteren Lösungsweg unmöglich.
-
Selektion
-
Die
select()-Routine, die viele Versionen von UNIXTM bereit
stellen, erlaubt es einem Prozess oder Thread, die Ausführung aufzuschieben,
bis Daten über
einen oder mehrere aktive Sockets, von denen jeder mit einem entfernten
Client verbunden ist, ankommen. Dieser Lösungsweg ist äußerst beschränkt. Die
mehrfachen Ereignisse, auf die gewartet wird, müssen jedes in irgendeiner Art
an Sockets oder Kommunikationskanäle gebunden sein. Keine andere
Art von Ereignissen kann aus dem select()-Anruf heraus ausbrechen.
-
Asynchrone I/O
-
Viele
Betriebssysteme bieten asynchrone I/O-Funktionalität an. Als
ein Beispiel stellt das Solaris®-Betriebssystem,
das vom Zessionar der vorliegenden Erfindung beziehbar ist, Asynchronous-Read
(aioread()) und Asynchronous-Write (aiowrite()) – Routinen bereit. Im Wesentlichen
benachrichtigt ein Signal den Prozess oder Thread, der mehrfache
asynchrone I/O-Anfragen ausgibt, wenn eine oder mehrere Anfragen
abgeschlossen sind.
-
Dieser
asynchrone Lösungsweg
kann I/O-Tätigkeiten
mehrfach ausnutzen. Andere Arten von Ereignissen erzeugen nicht
dieses Signal. Der asynchrone I/O-Lösungsweg ist dementsprechend
zu einschränkend.
-
Threads
-
Multithreading
breitet sich in Betriebssystemen aus und scheint im Wesen vieler
Systeme selbst zu liegen. Zum Beispiel unterstützen OS2® (erhältlich von
International Business Machines Incorporated aus Armonk, New York),
Solaris®,
NT® und
HP UX® (erhältlich von
Hewlett Packard aus Cupertino, California) Multithreading.
-
Der
derzeitige Posix Multithreading-Standard, Portable perating System
Interface, Part 2: System API – Amendment
2: Threads Extension (IEEE Std P1003.1c Draft 10, 1994) ("Posix.1c.") stellt eine Ereignis-Warte-Funktionalität (pthread_cond_wait())
bereit. Ein Thread wartet auf eine beliebige Ereignisbenachrichtigung (und
nimmt wahlweise ein Timeout, wenn die Benachrichtigung nicht innerhalb
einer vorab bestimmten Zeit auftritt). Jedoch unterstützen diese
Betriebssysteme und Posix.1c nicht das gleichzeitige Warten eines Threads
auf mehr als auf ein Ereignis.
-
WO-A-94/23365
offenbart die Ereignishandhabung in einer Computerumgebung. Anwendungsprogramme
sind mit einem Mechanismus zum Errichten einer Vorlage ausgestattet,
die ein Ereignisqualifizierungsnetzwerk genannt wird, welches das
Auftreten eines Ereignisses oder einer besonderen Verbindung von Ereignissen
gemeinsam mit praktisch jeder anderen Qualifizierungsbedingung,
die durch die Anwendung ausgewiesen ist, überwacht. Jedoch ist die Aufgabe
des Bestimmens, ob besondere Verbindungen von Ereignissen oder andere
Bedingungen aufgetreten sind, weiter auf die System-Software verwiesen.
Das Ereignisqualifizierungsnetzwerk auf der Anwendungs- oder Benutzerebene
meldet sein Interesse an einem Ereignis beim Ereignismanager an,
welcher Teil der Betriebssystem-Software ist, und wird dadurch bei
Auftreten des Ereignisses gewarnt.
-
OFFENBARUNG
DER ERFINDUNG
-
Die
Erfindung zielt darauf, Betriebssystemunterstützung zur wirkungsvollen Thread-Synchronisation mit
vielfachen gleichzeitigen Ereignissen für Anwendungen zu emulieren,
die auf Betriebssystemen laufen, die keine solche Unterstützung bereitstellen
(zum Beispiel UNIXTM). Die Erfindung unterstützt eine
M:N Beziehung zwischen Ereignissen und Threads, auch wenn das darunter
liegende Betriebssystem dies nicht tut. Jeder Thread, der blockiert,
wobei er auf Benachrichtigung von vielfachen Ereignissen wartet,
wird entsperrt, wenn die erste solche Benachrichtigung auftritt.
Jede vorliegende Ereignisbenachrichtigung kann mehr als einen Posix-Thread
entsperren.
-
Die
Erfindung umfasst ein Verfahren in einem Multithreading-Computersystem,
wobei das Verfahren umfasst: Erzeugen, auf einer Benutzerebene,
einer Sammlung von Datenstrukturen für einen ersten Thread; Sammeln
von Darstellungen einer ersten Mehrzahl von Ereignissen in der Sammlung
von Datenstrukturen; Einleiten des Sperrens, auf der Benutzerebene,
des ersten Threads auf der Grundlage der ersten Mehrzahl von Ereignissen;
Empfangen, auf der Benutzerebene, der Benachrichtigung des Auftretens
von einem oder mehreren Ereignissen; Prüfen, ob die Sammlung von Datenstrukturen
wenigstens eines des(der) aufgetretenen Ereignisse(s) umfasst; und
Einleiten des Freigebens, auf der Benutzerebene, des ersten Threads
bei Auftreten von wenigstens einem Ereignis, das in der Sammlung
von Datenstrukturen vorhanden ist.
-
Die
Erfindung umfasst auch ein Computersystem und ein Computerprogrammprodukt
zum Ausführen des
obigen Verfahrens.
-
KURZE BESCHREIBUNG
DER ZEICHNUNGEN
-
1 ist ein Schema eines Prozessors,
der die Erfindung eingebaut hat;
-
2A, 2B und 3 sind
Diagramme des Steuerungsflusses gemäß einer Ausführungsform
der Erfindung;
-
4 stellt grafisch die Beziehung
zwischen dem Container und den beinhalteten Alarmsammlungen dar.
-
BESCHREIBUNG
DER BEVORZUGTEN AUSFÜHRUNGSFROM
-
Obwohl
alle Codierungsbeispiele im Folgenden in der Sprache C++ vorliegen,
wird jeder Durchschnittsfachmann auf diesem Gebiet der Technik leicht
einsehen, dass jede andere Programmiersprache für allgemeine Zwecke (C, Java®, SmallTalk
usw.) hätte
gewählt
werden können.
Des Weiteren wird der erfahrene Praktiker verstehen, obwohl die
Beschreibung und die Codierungsbeispiele die Terminologie von Posix
und Posix.1c einsetzen, dass Posix und Posix.1c nur aktuelle Umsetzungen
der allgemeineren Betriebssystemkonzepte sind, die sie verwirklichen.
-
1 ist ein Prinzipskizze
eines Prozessors 100, der die Erfindung eingebaut hat.
Der Prozessor 100 umfasst eine CPU 150 und ein
Datenspeichermedium 130. Der Prozessor 100 umfasst
auch wahlweise Treiber 120, die durch einen Bus 140 miteinander
verbunden sind, und eine Benützerschnittstelle 110,
die mit den obigen Bauteilen durch einen Verbindungsstecker 160,
wie auf diesem Gebiet der Technik bekannt, verbunden ist.
-
Das
Datenspeichermedium 130 umfasst typischerweise ein Betriebssystem
(nicht gezeigt) und wenigstens einen Prozess (nicht gezeigt), wobei
beide auf der CPU 150 ausgeführt werden.
-
Überblick
-
Während der
Lebenszyklus eines Prozesses – oder
korrekter der Threads, die den Prozess ergeben – fortschreitet, treten viele
Ereignisse auf, mit denen solch ein Thread sich synchronisieren
will. Beispiele solcher Ereignisse umfassen den Ausgang eines Unterprozesses,
den Ausgang eines anderen Threads, den Abschluss von asynchroner
File-I/O, das Eintreffen
einer Fernclientnachricht über
das Netz und das Freigeben einer Ressource (angezeigt, indem ein
anderer Thread einen Posix-Mutex entsperrt oder das auf einem Posix-Conditional
signalisiert oder sendet).
-
Nach
dem Stand der Technik umfasst Synchronisation, dass ein (oder mehrere)
Thread(s) vorhanden ist(sind) und wartet(warten) ("blockiert", "Ausführung angehalten"), bis ein solches
Ereignis auftritt. Wenn das Ereignis letztlich auftritt, informiert
der Ereignisbenachrichtigungsmechanismus des darunter liegenden
Betriebssystems alle (wenn überhaupt)
Threads, die auf dieses Ereignis gewartet haben. Dementsprechend
stellt der Stand der Technik eine 1:N Beziehung zwischen Ereignissen
und Threads bereit. Jede beliebige Anzahl von Threads kann auf ein
Ereignis warten.
-
Im
Gegensatz dazu erzeugt die vorliegende Erfindung auf der Benutzerebene
eine M:N Beziehung zwischen Threads und Ereignissen. Jeder der M
Threads kann in jedem der N Ereignisse sperren und jedes der N Ereignisse
kann durch M Threads erwartet werden. Ein Alarmobjekt dient dazu,
um Posix-Threads und die Ereignisse, auf welche die Threads warten,
zusammenzufügen.
M Threads warten auf ein Alarmobjekt, das für N verschiedene Ereignisse
stehen kann:
M Threads: ein Alarmobjekt : N Ereignissen
-
Die
Alarmklasse umfasst zwei interne Sammlungen von Alarmen, von denen
normalerweise nur einer für
jedes beliebige Alarmobjekt aktiv ist. Alarmobjekte treten in zwei
Typen auf: als Container und als darin Beinhaltete. Jeder Typ kann "benachrichtigt" werden, wenn sein
Ereignis eingetreten ist.
-
Container-Alarm(Alert)objekt
-
Ein
Container Alarmobjekt stellt einen einzelnen Synchronisationspunkt
dar. Es weist eine Nicht-null-Sammlung von Alarmobjekten auf, von
denen jedes einem Ereignis entspricht.
-
Wenn
ein Thread wegen eines einzelnen Alarmobjekts des Containertyps
blockiert, wartet der Thread tatsächlich auf das logisch einschließende R
der Ereignisse, die durch alle Alarmobjekte im Container dargestellt
sind. Der Thread entsperrt, wenn eines dieser Ereignisse auftritt.
-
Da
der Container alle Ereignisse darstellt, mit denen ein Thread die
Absicht haben kann, sich an einem bestimmten Punkt zu synchronisieren,
hält sich
ein Thread typischerweise nur einen solchen Container. Jedoch kann,
aus Gründen
der Einfachheit der Programmierens, ein Thread sich eine Mehrzahl
solcher Container halten und nur auf den einen, der von Interesse
ist, an einem bestimmten Punkt warten.
-
Beinhaltetes Alarm(Alert)objekt
-
Ein
beinhaltetes Alarmobjekt stellt ein einzelnes Ereignis dar. Es weist
eine Nicht-null-Sammlung von Alarmobjekten auf, die alle Container-Alarmobjekte
umfasst, die diesen Alarm enthalten.
-
Alle
Ereignisobjekte, die einen beliebigen Typ von Ereignis (den Ausgang
eines Unterprozesses, den Ausgang eines anderen Threads, usw.) darstellen,
benachrichtigen ihr entsprechendes Alarmobjekt, wenn ihr jeweiliges
Ereignis auftritt.
-
Sobald
solch ein beinhaltetes Ereignisalarmobjekt benachrichtigt ist, benachrichtigt
es seinerseits alle Container-Alarmobjekte, deren Mitglied es ist.
Dies entsperrt seinerseits jeden Thread, der wegen eines Satzes
von Ereignissen, die durch einen dieser Container dargestellt wird,
blockiert war.
-
Durch
Ausnützen
derselben (Alarm-)Klasse, um sowohl die Container-Fälle als
auch die beinhalteten Fälle
zu handhaben, wird eine einzelne Benachrichtungsschnittstelle beibehalten,
so dass:
- – wenn
ein Thread auf seinen Warnung wartet, der Thread entsperrt, wenn
die Benachrichtigung auf einem der beinhalteten Ereignisse auftritt;
und
- – wenn
ein Ereignis seinen Alarm benachrichtigt, alle Threads, die auf
einen Satz von Ereignissen einschließlich dieses einen Ereignisses
warten, entsperren.
-
Diese
M:N Beziehung zwischen Alarmobjekten unterstützt daher eine Betriebssystem
unabhängige M:N
Beziehung zwischen Posix-Threads und Ereignissen.
-
Datenstrukturen
-
Die
Datenstrukturen und Protokolle, die in einer bevorzugten Ausführungsform
eingesetzt werden, um die Emulation der Synchronisation mit vielfachen,
unabhängigen
Ereignissen zu erreichen, werden im Folgenden beschrieben.
-
Alarm(Alert)klasse
-
Die
Erfindung verwendet ein Alarmobjekt, um das Warten und die Ereignisbenachrichtigung
zu bewirken. Eine bevorzugte Umsetzung der Alarmklasse findet sich
in Anhang A, hierin angefügt.
-
In
Bezug auf Ereignisbenachrichtigung kann das Auftreten einiger Ereignisse,
wie man sich vorstellen kann, einen blockierten Thread aufwecken.
Solche Ereignisse umfassen zum Beispiel den Abschluss von asynchronem
I/O, das Entsperren eines Mutex, den Ausgang eines Unterprozesses
oder externen Threads, das Ablaufen eines Zeitgebers und den Empfang
einer nicht angeforderten Client-Anfrage. Für jedes Ereignis, welches vorstellbar
einen blockierten Thread aufwecken kann, gibt es einen entsprechenden
Alarm-Client. Wie weiter unten erklärt, verwendet dieser Alarm-Client
ein internes Alarmobjekt, um die Benachrichtigung seines Auftretens
bekannt zu geben.
-
Alarmsammlungs(AlertCollection)klasse
-
In
Bezug auf Warten besitzt ein Thread die Fähigkeit zu blockieren, bis
die Benachrichtigung für
ein Container-Alarmobjekt
aufgegeben wurde, das eine Sammlung von Alarm-Clients darstellt. Anhang B gibt eine Beispielschnittstelle
für ein
Alarmsammlungsobjekt wieder, das intern durch den Container Alarmsammlung verwendet
werden könnte.
-
Die
Alarmsammlungsklasse schließt
in sich die Einzelheiten einer Sammlung von Alarmen ein. Die Alarmsammlungsklasse
kann eine Umsetzung einer Thread sicheren Sammlungsvorlage sein,
die mit einer beliebigen aus einer Anzahl von im Handel erhältlichen
Objektbibliotheken ausgestattet ist. "Tools.h++", welches vom Zessionar der vorliegenden
Erfindung erhältlich
ist, ist eine von solchen Objektbibliotheken.
-
Ein
Thread erzeugt ein Container-Alarmobjekt, um als ein Container für einen
Satz von anderen Alarmobjekten zu dienen, von denen jedes einem
unterschiedlichen Ereignis entspricht. Durch Blockieren, bis der Alarm-Container
benachrichtigt ist, blockiert der Thread tatsächlich, bis einer der Alarme
in der Container-Sammlung benachrichtigt worden ist.
-
Damit
dies auftreten kann, umfasst jedes Alarmobjekt intern zwei Alarmsammlungsobjekte,
Containerobjekte und beinhaltete Objekte, die Sammlungen von Alarmen
darstellen. Die erste Alarmsammlung, Containersammlung, stellt den
Satz von Objekten dar, für
welche der augenblickliche Alarm als ein Container dient. Die zweite
Alarmsammlung, die beinhaltete Sammlung, umfasst den Satz von Alarmobjekten,
welche diesen Alarm enthält.
-
4 stellt grafisch die Beziehung
zwischen den Container- und den beinhalteten Alarmsammlungen dar.
In 4 weist ein Alarmobjekt 510 den
internen Alarmsammlungen-Container 520 und die Contained
(Beinhalteten) 530 auf. Gleicherweise besitzen sowohl der
Thread_Ausgang_Ereignis-Alarm 511 als auch der Zeitgeber_Ablauf_Ereignis-Alarm 512 den
internen Alarmsammlungen-Container 521 und 531 beziehungsweise
Contained (Beinhaltet) #522 und 532. In diesem Beispiel
beinhaltet die Container-Alarmsammlung 520 des Alarmobjekts 510 sowohl
den Thread_Ausgang_Ereignis-Alarm 511 als auch den Zeitgeber_Ablauf_Ereignis-Alarm 512.
Dementsprechend zeigen sowohl die beinhalteten Alarmsammlungen 531 und 532,
dass ihre jeweiligen Alarme 511, 512 in der Container-Alarmsammlung 520 des
Alarmobjekts 510 enthalten sind.
-
Protokolle
-
Die
Alarmklasse weist die folgenden Verfahren auf: is_awake(), make_member(),
own(), notify_all() und wait_for_any(). Anhang A stellt eine mögliche Umsetzung
dieser Funktionen vor.
-
Tabelle
I listet auf, welche Verfahren typischerweise durch welchen Typ
Alarmobjekt eingesetzt werden:
-
-
Die
Routine is_awake() bestimmt für
einen besonderen beinhalteten Alarm, ob das Ereignis, das dem Alarm
entspricht, angemeldet worden ist, und für einen bestimmten Container-Alarm,
ob eines der Ereignisse, auf die gewartet wird, angemeldet worden
ist.
-
Make_member()
nimmt einen Container-Alarm als Eingabe und fügt ihn zur beinhalteten Alarmsammlung
hinzu. Vorzugsweise umfasst make_member() eine n-Ebenen-Schleifenerfassungslogik,
die verhindert, dass ein Alarm sich selbst beinhaltet. Solch eine
Logik ist auf diesem Gebiet der Technik gut bekannt. Ein Ein-Ebenen-Schleifenerfasser
ist hier dargestellt.
-
Die
own() Routine empfängt
einen beinhalteten Alarm als Eingabe und fügt das Ereignis der Container-Alarmsammlung
des 'dieser Alarm' hinzu und fügt das 'dieser Alarm' der beinhalteten
Alarmsammlung des besonderen Alarms hinzu. Wiederum werden alle
vernünftigen
Anstrengungen empfohlen, um Schleifen zu erfassen.
-
Da
alle Alarmobjektverbindungen intern auf dem neuesten Stand gehalten
werden, stellt diese Routine eine direkte Programmierschnittstelle
bereit. Ein Anwendungsprogramm kann ein Container-Alarmobjekt erzeugen
und Alarmobjekte, die den Ereignissen, auf die gewartet wird, entsprechen,
mittels der own() Routine hinzufügen.
-
Die
notify_all() Routine benachrichtigt alle Alarme, die den gegebenen
Alarm beinhalten, wie durch die Beinhalteten beschrieben. Auf Einzel-Thread
Weise mittels Synchronisation bestimmt notify_all() die Anzahl der
Alarme in der beinhalteten Sammlung. Notify_all() erhält dann
jeden Alarm einen nach dem anderen und ruft die notify_all() Routine
des erhaltenen Alarms auf, um alle Alarme, die den erhaltenen Alarm
enthalten, zu benachrichtigen. Das 'dieser Alarm' wird als aktiv markiert und alle Threads,
die zu dem Zeitpunkt auf Grund der Bedingungsvariablen, die mit
dem 'dieser Alarm' in Verbindung stehen,
blockiert sind, werden entsperrt.
-
Wenn
nun ein Thread Alarme in einen Container hinein Alarme gesammelt
hat, kann er auf jeden beliebigen von diesen mit der wait_for_any()
Routine jenes Alarm-Containers warten.
-
In
der Abfrageschnittstelle, die in Anhang B gezeigt ist, unterstützt die
interne Alarmsammlungsklasse datenfeldähnliches Datenauslesen durch
die get() Funktion, welche einen Index-Offset übergeben bekommt. Alternative
Lösungen
umfassen das Überlagern
des "[]" perators und Einsetzen
eines anderen auswählbaren Typs
Sammlungsklasse.
-
Die
Alarmsammlungsklasse unterstützt
auch das Hinzufügen
und Entfernen von Alarmen als auch das Zuweisen der Größe an eine
Alarmsammlung. Die Umsetzung der add(), get(), remove() und size()
Verfahren ist einfach und sollte normalerweise durch die Objektbibliothek
bereitgestellt sein.
-
Szenario
-
Betrachten
wir nun einen Thread, welcher sich mit dem Ausgang eines anderen
Threads oder dem Ablauf eines Zeitgebers synchronisieren möchte. Dieses
Beispielszenario wird im Folgenden genau erklärt.
-
2A, 2B und 3 sind
Diagramme des Steuerungsflusses gemäß einer Ausführungsform
der Erfindung. Der Thread erzeugt einen Thread_Exit_Event Alarmclient,
Schritt 205a und einen Timer_Expiration_Event Alarmclient,
Schritt 205c. Typischerweise erzeugt ein Thread einen Ereignisalarmclient
für jedes
Ereignis, auf das der Thread warten möchte, Schritt 205.
Mögliche
Alarmclients umfassen das Freigeben eines Mutex, den Ablauf eines
Zeitgebers, den Ausgang eines Unterprozesses und den Abschluss einer
I/O-Handlung.
-
Alarme
und Alarmclients werden durch Anordnen eines Alarmobjekts innerhalb
des Clients verbunden. Alternativ können die Clients öffentlich
von der Alarmklasse abgeleitet werden.
-
Anhang
C betrifft eine Ausführungsform
dieses Thread_Exit_Event Alarmclients. Die Umsetzung des Timer_Expiration_Event
Alarmclients entspricht der Umsetzung des Thread_Exit_Events.
-
Um
einen parallelen Ausführungsthread
zu schaffen oder ablaufen zu lassen, würde normalerweise der Thread
zum Beispiel die Posix.1c pthread_create() Routine oder diesem Gleichwertiges
in dem Betriebssystem, das hinter dem Prozess steht, aufrufen. Um
jedoch den Vorteil der Erfindung auszunutzen, verwendet die Anwendung
eine alternative API. Die alternative API würde das Aufbauen eines Thread_Exit_Event
Objekts, Schritt 230c, und das Ablaufen des Thread_Exit_Event
Objekts, Schritt 210, umfassen:
-
-
-
Beispiel 1: Code auf Anwendungsebene,
welcher einen parallelen Thread startet
-
Bevor
oder nachdem der neue Thread erzeugt und gestartet worden ist, Schritt 210,
kann die Anwendung das Ereignis, das dem Ausgang des Threads entspricht,
einer heterogenen Liste von solchen Ereignissen, auf die gewartet
werden muss, in einem Container Alarm hinzufügen, Schritt 215:
-
-
Beispiel 2: Code aus Anwendungsebene,
welcher den Ausgang eines parallelen Threads als ein Ereignis, auf das
gewartet wird, identifiziert
-
In
gleicher Weise kann der Prozess einen Zeitgeber setzen, Schritt 210,
und das Ereignis, das einem Ablauf eines Zeitgebers entspricht,
auf jene selbe heterogene Liste von Ereignissen, auf die gewartet
werden muss, hinzufügen,
Schritt 215:
-
-
Beispiel 3: Code auf Anwendungsebene,
welcher den Ablauf eines Zeitgebers als ein Ereignis identifiziert,
auf das gewartet wird
-
Wenn
ein Thread auf Grund der Sammlung von Ereignissen blockiert, z.
B. mittels des Posix.1c pthread_cond_wait() oder mittels Alarm-Routinen,
Schritt 220, wird der Thread entsperren, wenn einer der
gesammelten Alarme auftritt, Schritt 225:
-
-
Beispiel 4: Code auf Anwendungsebene,
welcher auf entweder den Ausgang eines Threads oder den Ablauf eines
Zeitgebers wartet
-
Wenn
ein Prozess Thread_Exit_Event::run() aufruft, durchläuft er die
erforderlichen Posix-Argumente: den pthread-Attributblock, ta, die Adresse der Thread-Funktion, thread_function,
und das Thread-Parameterargument, function_argument. Wie die Ausführungsform
in Anhang C zeigt, verwendet die run()-Funktion die Argumente, um
einen neuen Posix-Thread hervor zu bringen. Run() sichert den zugelieferten
Thread-Attributblock, Funktionszeiger und Funktionsargument; bringt
einen neuen Posix-Thread mit dem besonderen Thread-Attributblock
hervor; und arbeitet die statische Thread_Exit_Event::wrapper-Funktion
ab.
-
Ein
neuer Posix-Thread wird dadurch ausgelöst, der die wrapper()-Funktion
ausführt.
Wrapper() seinerseits ruft die durch den Benutzer beigestellte Funktion
mit dem vom Benutzer beigestellten Argument auf, Schritt 305.
-
Wenn
die Funktion abgeschlossen ist, kehrt die Steuerung zur wrapper()-Funktion
zurück,
die notify_all() auf seinem Thread_Exit_Event-Objekt aufruft, um
alle Threads, die auf diesen Steuerthread warten, abzuschließen, zu
entsperren, Schritt 310.
-
In
gleicher Weise benachrichtigt der Alarm bei seiner Aktivierung alle
seiner Alarm-Clienten von seiner Aktivierung, Schritte 305, 310.
-
Da
der Thread eher auf die Container-Alarm-Sammlung wartet als auf
einen einzelnen beinhalteten Alarm, gelingt entweder dem Tread-Ausgang-
oder dem Zeitgeber-Ablauf-Ereignis
das Entsperren des Threads. Daher hat sich der Thread auf das erste
der zwei Ereignisse, das aufgetreten ist, synchronisiert. Die Emulation
auf der Benutzerebene der gesamten Betriebssystemsunterstützung für die Synchronisation
auf mehrfache, unabhängige
und heterogene Benachrichtigungen wird daher ermöglicht.
-
In
der obigen Beschreibung sind einige Routinen aus Gründen der
Einfachheit so beschrieben, als empfingen sie ein Argument, das
ein bestimmter Datensatz ist, obwohl solch eine Routine einen Zeiger
auf den Datensatz oder einen mehrfach indirekten Zeiger auf den
Datensatz empfängt.
Ein Durchschnittsfachmann auf diesem Gebiet kann den entsprechenden
Ersatz wie erforderlich durchführen.
-
Natürlich kann
der Programmtext für
solch eine Software, wie sie hierin offenbart ist, in seiner statischen
Form auf einer magnetischen, optischen oder anderen Disk, auf Magnetband
oder einem anderen Medium existieren, welche Medienbewegung für das Speichern
und/oder Auslesen in ROM, in RAM oder in ein anderes Datenspeichermedium
erfordern. Jenes Datenspeichermedium kann in ein Computersystem
fix eingebaut oder einschiebbar sein.
-
Anhang A – Alarmschnittstellendefinition
und Abfrage-Implementierung
-
Obwohl
die Schnittstelle für
die Alarmklasse in der C++ Sprache festgelegt ist und den Posix.1c-Multithreadingstandard
verwendet, könnten
andere Sprachen (zum Beispiel C, Java oder SmallTalk IDL) und andere
Standards (zum Beispiel DCE-Threads oder Solaris®-Threads)
ausgewählt
werden.
-
A.1
Alarm-Schnittstellen-Definition
-
-
A.2 Alarm-Implementierung
-
Der
folgende Code zeigt mögliche
Implementierungen einiger der Funktionen, die in der Alarmschnittstelle
beschrieben sind.
-
-
-
-
Anhang B – Alarmsammlungsschnittstellendefinition
-
Die
Alarmsammlungs-Klasse trägt
in sich die Details einer Alarmsammlung und kann so gestaltet sein, um
eine Umsetzung einer Multithread-sicheren Sammlungsvorlage zu sein,
die von einer beliebigen Anzahl von Objektbibliotheken, z. B. "tools.h++", erhältlich vom
Zessionar der vorliegenden Erfindung, verfügbar ist.
-
-
Anhang C – Abfrageereignis:
Thread_Exit_Event
-
Die
definierte Thread_Exit_Event-Klasse ist ein "Client" der Alarm-Klasse. Jedes Thread_Exit_Event-Objekt
wird ein internes Alarm-Objekt benachrichtigen, wenn der Posix-Thread, den es darstellt,
zu Ende ist. Alle anderen Threads, die auf Grund dieses Ereignisses
blockieren, werden dann aufgeweckt.
-
-
-