Multithreading with Labview
Today we execute labview apps on CPU with hyper-threading capabilities and programmer must make every effort possible to achieve maximum task handling, optimisation and prioritisation. In order to further achieve improved reliability and execution time for the tasks, labview is equipped with 5 levels of ‘Execution Subsystems’. Each execution subsystem could be configured to handle programming tasks as threads operating at priority levels.
Labview Execution Subsystems
Labview have five subsystems which are User, Standard, I/O, DAQ, Other 1 and Other 2. Each subsystems can execute up to 40 threads at five priority level with each priority level able to handle up to 8 threads i.e 8 threads per priority level. Any VI could be run under any subsystem and by default vis run under User subsystem and labview often puts new subvi as ‘same as caller’ means User subsystem.
labview execution subsystem are vital for running threads at dedicated execution time. such performance of vi running on windows os is comparable with app execution on real time os, Khurram Waris said.
Configuring execution subsystem:
In order to configure execution subsystem, simply go to vi properties and make selection under ‘VI execution’ menu. Using good programming architecture, we can achieve excellent performance and dedicated execution rates. I often advise programmers to use a Module/API approach to labview architecture. Such architecture encapsulates complete ‘system part’ in to dedicated vis. For instance, sensor module could be deployed to handle all tasks related to sensors defined by sensor module API – initialise sensor, calibrate sensor, acquire sensor data etc. Similarly, we can have module for Display, Controller, Error, Front Panel, Instrument Control and as much as we wish to scale our labview application without jeopardising functionality of other modules.
Achieve dedicated execution time:
Once labview app architecture is designed in format aforementioned, programmer could easily change execution engine of main module vi and set desired priority. Lets say one wish to keep dedicated execution time for user interface, then main ‘Front Panel’ module vi could be placed under ‘Standard’ execution subsystem. As Front panel module vi is the only vi that handles front panel with all related subvis being called within Front Panel module vi, all of those called vis are set as ‘same as caller’ under ‘Preferred Execution System’. Such programming ensures all front panel vis are now handled by ‘Standard’ execution subsystem. Likewise, programmer can choose to run DAQ module under DAQ execution system. The I/O subsystem could be used to run RS232, TCP/IP or VISA vis which would certainly help with a dedicated thread pool and it also ensures some amount of execution time.
Prevent priority inversion using better labview architecture:
API/Module architecture as described above also prevents priority inversion scenario. Although, using subsystems and priority levels as described above could improve application execution significantly, a bad labview architecture might mean whole application would break and cause run time errors due to priority inversion issues. Lets take an example of an app that contains FunctionA.vi and FunctionB.vi running in execution subsystem Other 1 and Other 2 respectively. FunctionA.vi was set at ‘time-critical’ priority and FunctionB.vi was set at ‘Background’ priority. Now if FunctionA.vi calls a subvi in Other 2 subsystem then it has to wait for background priority thread in FunctionB.vi to get scheduled time to execute. Such scenario is usually described as priority inversion that could easily be created by a careless coder. However, programmer will never be found in such situation if careful labview coding architecture is in place.
Resource Starvation:
Resource starvation is another common scenario that might arise when one starts to play with thread priority levels and execution systems. For instance, lets assume threads FunctionA.vi and FunctionB.vi above calls another vi Child.vi and Child.vi is set as ‘same as caller’. Child.vi would then run by both FunctionA.vi or FunctionB.vi based on who calls it first. As FunctionA.vi is set at much higher priority, Child.vi would be accessed far more often by FunctionA.vi then FunctionB.vi. FunctionB.vi will get starved as it does not get much access to Child.vi.
Above are amongst the few functionalities achievable when coding labview. We can arrive to conclusion that a much prudent and planned approach to labview coding is often necessary when programming large applications to achieve app reliability.