In traditionellen Sprachen muss ein Programm in verschiedene Thread geteilt werden, um es parallel auszuführen. Zwar kann jeder Thread zur selben Zeit ablaufen, jedoch besteht ein Unterschied zwischen dem Schreiben von Code, der in einer Multithreading-fähigen Anwendung ablaufen kann, und Code, der parallel ausgeführt wird, denn nur der parallele Code nutzt die Leistungsfähigkeit von Multicore-Prozessoren optimal aus. Dieser Unterschied kommt oft in den Treibern oder Funktionen zum Ausdruck, die der Anwender eventuell für das Schreiben von Programmen verwendet. Multithreading-fähige Funktionen können aus mehreren Threads aufgerufen werden und überschreiben ihre Daten nicht, was Konflikte vermeidet, die durch das Blockieren der Ausführung hervorgerufen werden. Ruft ein Thread die Funktion auf, muss ein anderer Thread, der dasselbe versucht, solange warten, bis der erste Thread abgearbeitet ist. Ablaufinvariante Funktionen gehen noch einen Schritt weiter und machen es möglich, dass mehrere Threads dieselbe Funktion zur gleichen Zeit parallel aufrufen und ausführen. Beide Beispiele werden in einem Multithreading-fähigen Programm korrekt ausgeführt, doch geht das mit ablaufinvarianten Funktionen schneller, da diese gleichzeitig ablaufen.
Es gibt Fälle und Umgebungen, in denen unter Umständen eine nicht ablaufinvariante Funktion oder ein solches Programm notwendig sind, um Probleme mit dem Zugriff auf Funktionen zu vermeiden. Viele Multithreading-fähige Bibliotheken sorgen für ihre eigene „Sicherheit“, indem sie Ressourcen sperren. Das bedeutet, wenn ein Thread eine Funktion aufruft, dann wird diese Funktion oder sogar die gesamte Bibliothek gesperrt, so dass sie von keinem anderen Thread genutzt werden kann. In einer parallelen Ausführungssituation, wenn zwei verschiedene Codepfade bzw. Threads versuchen, ein und dieselbe Bibliothek bzw. Funktion zur gleichen Zeit aufzurufen, bedeutet dies, dass die Sperrung einen der Threads zum Stillstand bringt, bis der andere Thread vollständig ausgeführt wurde. Darüber hinaus wird Speicherplatz gespart, wenn nur ein Thread auf einmal auf eine Funktion zugreifen kann, da keine zusätzlichen Instanzen notwendig sind.
Jedoch kann, wie oben erwähnt, die Kombination paralleler Techniken mit Ablaufinvarianz in Funktionen die Leistung des Codes steigern.
Da Gerätetreiber mit LabVIEW (wie etwa NI-DAQmx) sowohl Multithreading-fähig als auch ablaufinvariant sind, kann eine Funktion von mehreren Threads gleichzeitig aufgerufen und immer noch korrekt ausgeführt werden, ohne zu blockieren. Dies ist eine wichtige Funktion zum Schreiben parallelen Codes und zum Optimieren der Leistung auf Multicore-Systemen. Falls Code ohne ablaufinvariante Ausführung benutzt wird, könnte das ein Grund dafür sein, dass die Leistung nicht besser geworden ist: Der Code muss warten bis die anderen Threads mit einer Funktion fertig sind, um sie aufrufen zu können. Dieser Punkt wird durch die LabVIEW-Funktion VI-Hierarchie (Show VI Hierarchy) verdeutlicht. Um die Hierarchie eines einzelnen VIs zu sehen, muss Ansicht >> VI-Hierarchie (View >> VI Hierarchy) gewählt werden. In der in Abbildung 1 dargestellten VI-Hierarchie hängen F1 und F2 vom selben VI ab (in diesem Fall ein sehr rechenintensiver Algorithmus für die Fast-Fourier-Transformation). Wenn F1 und F2 parallel ausgeführt werden sollen, ist es wichtig, dass dieses VI ablaufinvariant ist.
Abbildung 1: Ansicht der VI-Hierarchie in LabVIEW
Um ein LabVIEW-VI ablaufinvariant zu machen, ist aus dem Dropdown-Menü Datei >> VI-Einstellungen (File >> VI Properties) und dann Ausführung (Execution) zu wählen. Jetzt kann man das Kästchen neben Ablaufinvariante Ausführung (Reentrant Execution) anklicken und eine Kopie-Option wählen.
Abbildung 2: Option für die ablaufinvariante Ausführung im Dialogfeld VI-Einstellungen
Mit der Option Kopien zwischen Instanzen austauschen (Share clones between instances) lässt sich die Speicherauslastung im Zusammenhang mit der Vorbelegung zahlreicher Klon-VIs reduzieren. Bei Auswahl dieser Option erstellt LabVIEW erst dann ein Klon-VI, wenn ein VI das ablaufinvariante VI aufruft. LabVIEW erstellt Klon-VIs also erst auf Nachfrage, was bei der Ausführung des VIs zu Jitter führen kann. LabVIEW erhält Statusinformationen nicht über alle Aufrufe des ablaufinvarianten VIs hinweg.
Bei der Anbindung an Hardware ist es wichtig, Thread-sichere und ablaufinvariante Treiber zu benutzen. Damit ist es möglich, in Form von Leistungssteigerungen von der Multicore-Technologie zu profitieren.
Bei früheren LabVIEW-Versionen sind Gerätetreiber nicht in allen Fällen ablaufinvariant. Der traditionelle Treiber NI-DAQ z. B. ist insoweit Multithreading-fähig, dass keine Fehlermeldung verursacht wird, wenn zwei verschiedene Threads gleichzeitig auf ihn zugreifen. Der Treiber reagiert auf diese Anfrage mit einer Sperrung, d. h. sobald ein Thread eine NI-DAQ-Funktion aufruft, werden alle anderen Threads in eine Warteposition gesetzt, bis die Funktion komplett ausgeführt wurde.
NI-DAQmx lässt sich hingegen wesentlich eleganter in einer parallelen Multithreading-fähigen Umgebung einsetzen. NI-DAQmx ist ablaufinvariant und erlaubt das gleichzeitige Aufrufen durch mehrere Threads. So können zwei verschiedene Analogeingänge von zwei verschiedenen Karten in zwei verschiedenen Threads im gleichen Programm ausgeführt werden. Beide laufen simultan ab, ohne dass es zu einer Blockierung kommt. Darüber hinaus können ein Analog- und ein Digitaleingang von zwei ganz verschiedenen Threads auf derselben Karte zur gleichen Zeit ausgeführt werden. Dabei wird aufgrund der Fähigkeiten des Treibers NI-DAQmx eine einzige Hardwarequelle eigentlich als zwei verschiedene Quellen behandelt.
Treiber für modulare Messgeräte von NI funktionieren wie NI-DAQmx. Alle in Tabelle 1 aufgeführten Treiber sind sowohl Thread-sicher als auch ablaufinvariant. Sie ermöglichen alle, dieselbe Funktion zweimal auf zwei verschiedenen Geräten zur selben Zeit aufzurufen. Besonders für große Systeme mit Programmcode, der auf mehrere Messgeräte zugreift, ist das von Vorteil.
Tabelle 1: Thread-sichere und ablaufinvariante Treiber für modulare Messgeräte