Die TAA bietet verschiedene Möglichkeiten, bestimmte Aufgaben innerhalb eines (technischen ) Geschäftsvorfalls parallel zu verarbeiten. Dieses Dokument solle eine Übersicht der diese Möglichkeiten bieten, mit besonderer Berücksichtigung der Implementierung in .NET.
Innerhalb einer Steuerung können durch die Verwendung des Gabelungskonstrukts Prozesse modelliert werden, die parallel ausgeführt werden sollen. Erst wenn alle Prozesse durchlaufen sind, wird die Verarbeitung der Steuerung beim Nachfolgekonstrukt, in diesem Fall das Entscheidungskonstrukt, fortgesetzt. Die einzelnen Prozesse arbeiten auf einer Kopie der TAA-Objektdaten, die erst bei Fertigstellung eines Prozesses wieder übernommen werden.
Ob die Prozesse zur Laufzeit auch parallel ausgeführt werden, kann über den Eintrag SpawnProcesses in der Config-Section der TAA-Registry gesteuert werden.
Grundsätzlich funktioniert die Parallelverarbeitung nur innerhalb eines Arbeitsgangs unter Verwendung der LAN-Infrastruktur. Auf der Geschäftsvorfall-Ebene wird noch keine Parallelverarbeitung unterstützt, 1) und werden solche Prozesse sequentiell abgearbeitet. Auch wenn die Steuerung als generiertes COBOL-Modul ausgeführt wird, werden die Prozesse sequentiell ausgeführt.
Auf Modulebene kann eine Parallelverarbeitung durch die Methode Spawn angestoßen werden. Im Gegensatz zu einem Call wird nicht auf die Fertigstellung des gerufenen Moduls gewartet, sondern wird der Aufruf asynchron durchgeführt. Das gerufene Modul bekommt eine Kopie der TAA-Objektdaten übergeben. Bei Fertigstellung des asynchron gerufenen Moduls bewahrt die Infrastruktur die Ergebnisse auf. Um die eventuell geänderten Daten zu übernehmen, muss durch einen Aufruf der Methode WaitFor auf die Fertigstellung gewartet werden.
Der Aufrufer kann auch verlassen werden, ohne auf die Fertigstellung zu warten. In dem Fall werden die Ergebnisse der asynchron gerufenen Module von der Infrastruktur verworfen.
Der asynchrone Aufruf kann in C/C++ und .NET 2) verwendet werden. In COBOL steht dies nicht zur Verfügung.
In .NET gibt es zusätzlich die Möglichkeit, bei einem asynchronen Aufruf eine Methode (Delegate) anzugeben, welche von der Infrastruktur bei der Fertigstellung ausgelöst wird. Diese Methode wird immer auf demselben Thread ausgelöst, in dem auch der Aufruf stattgefunden hat. Sie ist insbesondere für Interaktionen gedacht, um den UI-Thread während eines Aufrufs nicht zu blockieren.
Bei der Verwendung von Threads in der Implementierung eines Moduls ist zu berücksichtigen, dass die TAA nur einen Thread innerhalb der Implementierung kennt. Damit die TAA weitere Threads erkennen kann, sollte am Anfang die Methode ThreadAttach an TeamWiSE.TAA.Context 3) ausgelöst werden. Über die Methode <TeamWiSE.TAA.Context> ThreadDetach wird der Thread wieder abgemeldet. Bei Threads, die von der Infrastruktur angelegt werden (bspw. für SpawnModule) wird dies automatisch gemacht.
Außerdem müssen in der Implementierung eines Moduls selbst Sperrmechanismen programmiert werden, damit gleichzeitige Zugriffe auf TAA-Datenobjekte verhindert werden, wenn Änderungen stattfinden können, sei es durch andere Threads oder durch Modulaufrufe.
.NET stellt eine ganze Reihe von Möglichkeiten zur asynchronen Verarbeitung zur Verfügung, bei denen nicht direkt erkennbar ist, dass mehrere Threads verwendet werden. Es ist darum besondere Vorsicht vonnöten, wenn diese Möglichkeiten verwendet werden. Daher folgt jetzt eine Auflistung der .NET-Objekte, die innerhalb einer Modulimplementierung verwendet werden können, und was bei einer multithreaded-Implementierung zu berücksichtigen ist.
Außerdem werden zu der jeweiligen Klasse alle Methoden und Eigenschaften aufgelistet, mit der Angabe, ob diese threadsafe bzw. threadaffin sind. Eine Funktion kann sein:
Ein Objekt bzw. eine Methode oder Eigenschaft ist threadaffin, wenn es/sie Verhalten enthält, durch das Funktionalitäten an einen spezifischen Thread gebunden sind; dies ist z.B. der Fall, wenn die Methoden des Objekts ausschließlich in dem Thread verwendet werden können, in dem dieses erzeugt wurde.
Es wäre vorstellbar, in Zukunft die Thread-Affinität einzelner Objekt und Methoden in den entsprechenden TAA-Klassen zu berücksichtigen, um bei Verletzung der Regeln eine entsprechende Ausnahmebehandlung 4) auszulösen.
In den nachfolgenden Aufstellung ist zu beachten, dass alle als „bedingt threadsafe“ markierten Eigenschaften und Methoden eigentlich als „nicht threadsafe“ zu betrachten sind. Eine darartige dreistufige Aufstellung ist auch eher unüblich. Die Angabe „bedingt Threadsafe“ steht an Stellen, wo wir vermuten, dass keine Daten für die Eigenschaft oder Methode benötigt werden, die parallel auf einem anderen Thread einen Einfluss auf die jeweilige Methode oder Eigenschaft haben könnten. So ist z.B. eine Änderung der Debug-, Enduser- oder TimestampOffset-Einstellungen eine denkbare (wenn auch unwahrscheinliche) Operation, bei der eigentlich alle „bedingt threadsafe“-Angaben sofort als „nicht threadsafe“ eingestuft werden müssten.
Die Klasse TeamWiSE.TAA.taaEnv stellt eine Reihe von statischen Methoden und Eigenschaften zur Verfügung, damit eine Modulimplementierung sich bei der TAA anmelden kann (taaEnter, TaaRegister), bzw. auf Metadaten oder sonstige Informationen zugreifen kann.
Methode/Eigenschaft | threadsafe | threadaffin |
cndClear() | nein | nein |
cndHighestSeverity | nein | nein |
cndRemove() | nein | nein |
chdShowAll() | nein | nein |
Conditions | nein | nein |
EndUser | ja | nein |
RegisterProcess() | ja | nein |
ResourceNames() | ja | nein |
Stage | ja | nein |
taaEnter() | ja | ja |
taaRegister() | ja | ja |
taaReRegister() | ja | ja |
UnregisterProcess() | ja | nein |
Methoden, die Metadaten liefern | ja | nein |
Der Einstieg in die TAA soll nur noch über die TeamWiSE.TAA.Context-Methoden taaEnter bzw. taaRegister erfolgen. Die bestehenden taaEnter/taaRegister-Methoden an der Klasse TeamWiSE.TAA.taaEnv sollen durch die Context-Methoden ersetzt werden.
Die Context-Instanz, die von diesen Methoden geliefert wird, kenne eine Eigenschaft ModlEnv, die der bisher bekannten ModlEnv-Instanz entspricht. Die Klasse ModlEnv ist nicht gegen eine threadübergreifende Nutzung abgesichert.
Nachfolgend sind die Methoden und Eigenschaften der Klasse ModlEnv beschrieben.
Methode/Eigenschaft | threadsafe | threadaffin |
ActiveEvent | ja | nein |
ApplicationSettings | ja | nein |
Bc | ja | nein |
Bp | ja | nein |
CallModule() | bedingt | nein |
cndAction | bedingt | nein |
cndClassHdlClear() | bedingt | nein |
cndClassHdlIdGet() | bedingt | nein |
cndClassHdlIdSet() | bedingt | ja |
cndClassHdlIsControl() | bedingt | nein |
cndClassHdlIsId() | bedingt | nein |
cndClassHdlIsProc() | bedingt | nein |
cndClassHdlPop() | bedingt | ja |
cndClassHdlProcGet() | bedingt | nein |
cndClassHdlProcSet() | bedingt | ja |
cndClassHdlPush() | bedingt | ja |
cndClassHdlReplace() | bedingt | ja |
cndClassHdlReset() | bedingt | ja |
cndHdlClear() | bedingt | nein |
cndHdlIdGet() | bedingt | nein |
cndHdlIdSet() | bedingt | ja |
cndHdlIsControl() | bedingt | nein |
cndHdlIsId() | bedingt | nein |
cndHdlIsProc() | bedingt | nein |
cndHdlPop() | bedingt | ja |
cndHdlProcGet() | bedingt | nein |
cndHdlProcSet() | bedingt | ja |
cndHdlPush() | bedingt | ja |
cndHdlReplace() | bedingt | ja |
cndHdlReset() | bedingt | ja |
cndMarkedForRaise() | bedingt | nein |
cndRaise() | bedingt | ja |
cndRaised | bedingt | nein |
cndRaiseMarked() | bedingt | ja |
cndSet() | ja | nein |
ctvEnv() | bedingt | ja |
DataSet | bedingt | nein |
InitStartRequest() | ja | nein |
IsReentry | ja | nein |
IsRegistered | ja | nein |
Name | ja | nein |
objDeclare() | bedingt | nein |
SpawnModule() | bedingt | nein |
State() | ja | nein |
taaLeave() | bedingt | ja |
TAAObjects | bedingt | nein |
ToString() | ja | nein |
Unregister() | bedingt | a |
UseDateTimeForTimestamps | bedingt | nein |
UseTimestamp | bedingt | nein |
WaitFor() | bedingt | ja |
WarnLevel | bedingt | nein |
Yield() | bedingt | ja |
Die Klasse TAAObject ist nicht gegen eine threadübergreifende Nutzung abgesichert. Die Instanz bleibt aber nach einem Modulaufruf oder auch einem Yield erhalten. Ein Modulaufruf auf einem anderen Thread kann den Inhalt jedoch ändern. Daher kann eine threadübergreifende Nutzung nur zum Teil über die C#-lock-Anweisung abgesichert werden. Denkbar wäre eine künftige Erweiterung, um den threadübergreifenden Zugriff mittels TAAObject-eigenen Lock/Unlock-Methoden abzusichern.
Unterschiedliche Instanzen können aber ohne Probleme auf unterschiedlichen Threads verwendet werden.
Methode/Eigenschaft | threadsafe | threadaffin |
DataTable | bedingt | Nein |
FillFromDataTable() | bedingt | Nein |
Name | Ja | Nein |
objAdd() | bedingt | Nein |
objAddList() | bedingt | Nein |
objCheck() | bedingt | Nein |
objCheckField() | bedingt | Nein |
objClassName | bedingt | Nein |
objCompleteRef() | Nein | Nein |
objCopy() | bedingt | Nein |
objCount | bedingt | Nein |
objCurrency | bedingt | Nein |
objDclName | Ja | Nein |
objDelete() | bedingt | Nein |
objDestroy() | Nein | Nein |
objDump() | Bedingt | Nein |
objExist | bedingt | Nein |
objFirst() | Bedingt | Nein |
objGetField() | bedingt | nein |
objInsert() | bedingt | nein |
objInsertList() | bedingt | nein |
objIsBOL | bedingt | nein |
objIsCurrenncy() | bedingt | nein |
objIsEmpty | bedingt | nein |
objIsEOL | bedingt | nein |
objIsValid | ja | nein |
objLast() | bedingt | nein |
objLength | bedingt | nein |
objNew() | bedingt | nein |
objNext() | bedingt | nein |
objPosition | bedingt | nein |
objPrev() | bedingt | nein |
objPutField() | bedingt | nein |
objReset() | bedingt | nein |
objSkz | nein | nein |
objSort() | bedingt | nein |
objStructureName | bedingt | nein |
objTypeName | bedingt | nein |
objValidateField() | bedingt | nein |
this[] (indexer) | bedingt | nein |
TimestampFormat | ja |
Die Klassen BcObject und BpObject können grundsätzlich threadübergreifend verwendet werden. Sämtliche Methoden und Eigenschaften dieser Objekte sind jedoch von Fall zu Fall auf Threadsafety zu prüfen. So kann bspw. die Eigenschaft einer Workflowproperty zwar threadsafe abgefragt werden, es ist aber durch zusätzliche (anwendungsseitige) Mechanismen zu gewährleisten, dass Schlussfolgerungen aus der Abfrage eines Wertes gegen parallele Änderung der gleichen Workflowproperty abgesichert werden.
Die Klasse TAACondition ist von der .NET Klasse System.Exception abgeleitet, und ist nicht gegen einen threadübergreifenden Zugriff abgesichert. Conditions können nur auf dem Thread abgefangen werden, auf dem sie aufgeworfen werden. Der Zugriff auf eine Liste von Conditions (ModlEnv.cndRaised, taaEnv.Conditions) erstellt einen Snapshot der aktuellen Liste. Dieser Snapshot ist aber nur bedingt threadsafe, denn wenn eine Condition aus der Liste in einem anderen Thread gelöscht wird, wird dies nicht festgestellt, und kann u.U. zu einem Laufzeitfehler führen.
Methode/Eigenschaft | threadsafe | threadaffin |
Associations | nein | nein |
cndCode | nein | nein |
cndGetArg() | nein | nein |
cndGetAssoc() | nein | nein |
cndGroup | nein | nein |
cndHelpContextID | nein | nein |
cndHelpFile | nein | nein |
cndID | nein | nein |
cndImpl | nein | nein |
cndLine | nein | nein |
cndLog() | nein | nein |
cndMessage | nein | nein |
cndModule | nein | nein |
cndRaise() | nein | ja |
cndRemove() | nein | ja |
cndSetArg() | nein | nein |
cndSetAssoc() | nein | nein |
cndSeverity | nein | nein |
cndShow() | nein | nein |
cndTimestamp | nein | nein |
cndTitle | nein | nein |
Message | nein | nein |
Severity() | ja | nein |
ToString() | nein |
Die Klassen in den Namespaces TeamWiSE.Services bzw. TeamWiSE.TAA.MetaData können generell threadübergreifend verwendet werden. Daher wird hier auf eine Auflistung der einzelnen Methoden und Eigenschaften verzichtet.
Der Zugriff auf CTV ist nicht threadübergreifend abgesichert, und sollte auch nicht threadübergreifend verwendet werden. Daher wird hier auf eine Auflistung der einzelnen Methoden und Eigenschaften verzichtet.