Nice Threads

Multithread programming in traditional languages can be a challenge. The programmer is responsible for managing many things, such as:

  • Separating logical tasks
  • Avoiding race conditions
  • Avoiding thread starvation conditions
  • Managing communication between the threads

LabVIEW, on the other hand, lends itself easily to multithreaded programming. In fact, if you follow the dataflow rules, multithreading will happen automatically when LabVIEW finds the opportunity.

Dataflow Programming

LabVIEW follows a programming model called dataflow programming. Rather than writing a program in the form of Step 1, Step 2, Step 3, …, LabVIEW programs pass the data on wires. Any VI taking inputs must wait for all of its inputs to be ready before it can run. In the example below, Loop A must run before Loop B is able to run, because Loop A produces data that is used by Loop B. The data dependency controls the order of execution.

Loops with a data dependency

Loops with a data dependency

However, if we remove the data dependency, we no longer force the order of execution. LabVIEW gets to decide which loop will run first. But is it Loop A or Loop B? Actually, it is both. This is where LabVIEW’s automatic multithreading kicks in. LabVIEW will create multiple threads and run each loop simultaneously, one in each thread.

Loops with no data dependency

Loops with no data dependency

Sharing Data

Remember, LabVIEW can do automatic multithreading only because there is no wired data dependency between the two loops, so the next question is, how do we share data between the two loops?

Local Variable

The first way of sharing data between loops that probably comes to mind is a local variable. It provides a way for you to access the value of data without wiring, so it can indeed be used and still allow automatic multithreading. But there are some drawbacks to local variables: they can cause race conditions, they need to be initialized, they break the dataflow programming paradigm, so you should not get used to using them (if you are a LabVIEW purist, you would never use a local variable).

Queues

A better way to share data between loops is with a queue. One loop can send a message to the other with a queue without creating a data dependency. And the message is delivered in a more predictable manner than a local variable. A great example of this technique is the standard Producer/Consumer template.

Producer-Consumer Template

Producer-Consumer Template

Spinning Off Threads

Multithreading with multiple parallel loops is fine, but it takes you only so far. Take for example a program that needs to capture data from a DAQ card as fast as possible, handle the user interface at a medium rate to keep up with the operator, and log the data to a file as a background function. Using parallel loops, you cannot control the priority of the various loops, which means the file loop runs as an equal to the DAQ task. That will likely slow down the DAQ and cause data to be lost.

One solution is to put each of the loops into a separate VI and “spin off” each of those VI’s to run independently. You can control the priority of a VI (under VI Properties->Execution), so you can set the DAQ VI to be time critical and the file I/O to be a background task.

How to Spin Off a Thread

AppControlPalette

To run a VI as a thread, you can’t just call it as you would a normal VI. Normally, when a VI calls another, that VI suspends its execution until the sub-VI completes. That’s the opposite of the multithreaded operation we are after. To spin off a VI, we can make use of some of the VI Server VI’s. These can be found in the Application Control palette.

The process is not difficult:

  1. Open VI Reference
  2. Invoke Node – Invoke Run VI, specifying to not Wait Until Done
  3. (Optionally) Invoke Node – Invoke FP.Open to show the front panel of the VI you are spinning off
  4. Use a named queue to communicate between the threads
  5. When the thread is terminated, use Close Reference to dispose of the reference to the VI

VI Server Functions

Priority

When you spin off a thread, it will run with the priority specified in VI Properties->Execution. You can also set the Preferred Execution System to tweak performance.

Leave a Reply