Company Events Academic NI Developer Zone Support Solutions Products & Services Contact NI MyNI

Suggestions for Using Execution Systems and Priorities

LabVIEW 8.5 Help
August 2007

NI Part Number:
371361D-01

»View Product Info

Following is a summary of some general suggestions about using the execution system options described in this document.

In most applications, it is not necessary to use priority levels or an execution system other than the Standard execution system, which automatically handles the multitasking of the VIs.

By default, all VIs run in the Standard execution system at Normal priority. In a multithreaded application, a separate thread handles user interface activity, so the VIs are insulated from user interface interaction. Even in a single-threaded application, the execution system alternates between user interface interaction and execution of the VIs, giving similar results.

In general, the best way to prioritize execution is to use Wait functions to slow down lower priority loops in the application. This is particularly useful in loops for user interface VIs because delays of 100 to 200 ms are barely noticeable to users.

If you use priorities, use them cautiously. If you design higher priority VIs that operate for a while, consider adding waits to those VIs in less time-critical sections of code so they share time with lower priority tasks.

Be careful when manipulating global variables, local variables or other external resources that other tasks change. Use a synchronization technique, such as a functional global variable or a semaphore, to protect access to these resources.

Simultaneously Calling SubVIs from Multiple Places

By default, VIs are not reentrant and the execution system will not run multiple calls to the same subVI simultaneously. If you try to call a subVI that is not reentrant from more than one place, one call runs and the other call waits for the first to finish before running. In reentrant execution, calls to multiple instances of a subVI can execute in parallel with distinct and separate data storage. If the subVI is reentrant, the second call can start before the first call finishes running. In a reentrant VI, each instance of the call maintains its own state of information. Then, the execution system runs the same subVI simultaneously from multiple places. Reentrant execution is useful in the following situations:

  • When a VI waits for a specified length of time or until a timeout occurs
  • When a VI contains data that should not be shared among multiple instances of the same VI

To make a VI reentrant, select File»VI Properties, select Execution in the VI Properties dialog box, and place a checkmark in the Reentrant execution checkbox.

When you interactively open a reentrant subVI from the block diagram, LabVIEW opens a clone of the VI instead of the source VI. The title bar of the VI contains (clone) to indicate that it is a clone of the source VI.

Note  Because you cannot perform source control operations on the clone of a source VI, LabVIEW dims the source control operation items in the Tools»Source Control menu of the clone VI.

You can use the front panels of reentrant VIs the same way you can use the front panels of other VIs. To view the original front panel of a reentrant VI from a clone of the reentrant VI, select View»Browse Relationships»Reentrant Original. Each instance of a reentrant VI has a front panel. You can use the VI Properties dialog box to set a reentrant VI to open the front panel during execution and optionally reclose it after the reentrant VI runs. You also can configure an Event structure case to handle events for front panel objects of a reentrant VI. The front panel of a reentrant VI also can be a subpanel.

You can use the VI Server to programmatically control the front panel controls and indicators on a reentrant VI at run time; however, you cannot edit the controls and indicators at run time. You also can use the VI Server to create a new reentrant instance of the front panel of a reentrant VI at run time. To open a new instance of the front panel of a reentrant VI, use the Open VI Reference function by wiring a strictly typed VI reference to the type specifier input. Use the Run VI method to prepare a VI for reentrant run by wiring 0x08 to the options input.

Types of Reentrant Execution

LabVIEW supports two types of reentrant VIs. On the Execution Properties page, place a checkmark in the Reentrant execution checkbox to enable the two reentrant VI options. Select the Preallocate clone for each instance option if you want to create a clone VI for each call to the reentrant VI before LabVIEW calls the reentrant VI, or if a clone VI must preserve state information across calls. For example, if a reentrant VI contains an uninitialized shift register or a local variable, property, or method that contains values that must remain for future calls to the clone VI, select the Preallocate clone for each instance option. Also select the Preallocate clone for each instance option if the reentrant VI contains the First Call? function. You also can use this option for VIs that are to run with low jitter on LabVIEW Real-Time.

Select the Share clones between instances option to reduce the memory usage associated with preallocating a large amount of clone VIs. When you select the Share clones between instances option, LabVIEW does not create the clone VI until a VI makes a call to the reentrant VI. With this option, LabVIEW creates the clone VIs on demand, potentially introducing jitter into the execution of the VI. LabVIEW does not preserve state information across calls to the reentrant VI.

The following table explains the memory usage and execution speed effects to consider when you select a reentrant VI type.

Reentrant VI Type Memory Usage Execution Speed
Preallocate clone for each instance Creates a clone VI for each call to the reentrant VI. Increases memory usage. Execution speed is constant.
Share clones between instances Only allocates clone VIs for the maximum number of simultaneous calls to the reentrant VI. Decreases memory usage. Creates clone VIs on demand. Slightly decreases execution speed and speed may vary per call.

Examples of Reentrant Execution

The following two sections describe examples of reentrant VIs that wait and do not share data.

Using a VI that Waits

The following illustration describes a VI, called Snooze, that takes hours and minutes as input and waits until that time arrives. If you want to use this VI simultaneously in more than one location, the VI must be reentrant.

The Get Date/Time In Seconds function reads the current time in seconds, and the Seconds To Date/Time function converts this value to a cluster of time values (year, month, day, hour, minute, second, and day of week). A Bundle function replaces the current hour and minute with values that represent a later time on the same day from the front panel Time To Wake Me cluster control. The Wake-up Time in Seconds function converts the adjusted record back to seconds, and multiplies the difference between the current time in seconds and the future time by 1,000 to obtain milliseconds. The result passes to a Wait (ms) function.

The Lunch VI and the Break VI use Snooze as a subVI. The Lunch VI, whose front panel and block diagram are shown in the following illustration, waits until noon and displays a front panel to remind the operator to go to lunch. The Break VI displays a front panel to remind the operator to go on break at 10:00 a.m. The Break VI is identical to the Lunch VI, except the display messages are different.

For the Lunch VI and the Break VI to run in parallel, the Snooze VI must be reentrant. Otherwise, if you start the Lunch VI first, the Break VI waits until the Snooze VI wakes up at noon, which is two hours late.

Using a Storage VI Not Meant to Share Its Data

If you make multiple calls to a subVI that stores data, you must use reentrant execution. For example, you create a subVI, ExpAvg, that calculates a running exponential average of four data points.

Another VI uses the ExpAvg subVI to calculate the running average of two data acquisition channels. The VI monitors the voltages at two points in a process and displays the exponential running average on a strip chart. The block diagram of the VI contains two instances of the ExpAvg subVI. The calls alternate — one for Channel 0, and one for Channel 1. Assume Channel 0 runs first. If the ExpAvg subVI is not reentrant, the call for Channel 1 uses the average computed by the call for Channel 0, and the call for Channel 0 uses the average computed by the call for Channel 1. By making the ExpAvg subVI reentrant, each call can run independently without sharing the data.

Debugging Reentrant VIs

To allow debugging on a reentrant VI, select File»VI Properties to display the VI Properties dialog box, select Execution from the pull-down menu, and place a checkmark in the Allow debugging checkbox.

When you open a reentrant subVI from the block diagram, LabVIEW opens a clone of the VI instead of the source VI. The title bar of the VI contains (clone) to indicate that it is a clone of the source VI. You cannot edit the clone VI. You can use the block diagram of the copy of the reentrant VI for debugging purposes; however, you cannot edit the block diagram instance. Within the block diagram, you can set breakpoints, use probes, enable execution highlighting, and single-step through execution. When you debug a reentrant VI with the Share clones between instances option selected on the Execution Properties page, do not set breakpoints, use probes, or enable execution highlighting in the clone VI. The clone VI does not maintain the debugging settings across calls. If you set the debugging settings in the original reentrant VI, the clone VIs maintain the original debugging settings.

If you need to edit a reentrant VI, you must open the original reentrant VI instead of the clone. You can open the reentrant VI from the clone by selecting Operate»Change to Edit Mode. LabVIEW opens the reentrant VI in edit mode. Alternatively, you also can select View»Browse Relationships»Reentrant Original. After LabVIEW opens the reentrant VI, select Operate»Change to Edit Mode to make the VI editable.

Note  When you debug applications and shared libraries, you cannot debug reentrant panels that an Open VI Reference function creates. You also cannot debug reentrant panels that are entry points to LabVIEW built shared libraries.

Synchronizing Access to Global and Local Variables and External Resources

Because the execution system can run several tasks in parallel, you must make sure global and local variables and resources are accessed in the proper order.

Preventing Race Conditions

You can prevent race conditions in one of several ways. The simplest way is to have only one place in the entire application through which a global variable is changed.

In a single-threaded application, you can use a Subroutine priority VI to read from or write to a global variable without causing a race condition because a Subroutine priority VI does not share the execution thread with any other VIs. In a multithreaded application, the Subroutine priority level does not guarantee exclusive access to a global variable because another VI running in another thread can access the global variable at the same time.

Functional Global Variables

Another way to avoid race conditions associated with global variables is to use functional global variables. Functional global variables are VIs that use loops with uninitialized shift registers to hold global data. A functional global variable usually has an action input parameter that specifies which task the VI performs. The VI uses an uninitialized shift register in a While Loop to hold the result of the operation. The following illustration shows a functional global variable that implements a simple count global variable. The actions in this example are initialize, read, increment, and decrement.

Every time you call the VI, the block diagram in the loop runs exactly once. Depending on the action parameter, the case inside the loop initializes, does not change, incrementally increases, or incrementally decreases the value of the shift register.

Although you can use functional global variables to implement simple global variables, as shown in the previous example, they are especially useful when implementing more complex data structures, such as a stack or a queue buffer. You also can use functional global variables to protect access to global resources, such as files, instruments, and data acquisition devices, that you cannot represent with a global variable.

Semaphores

You can solve most synchronization problems with functional global variables, because the functional global VI ensures that only one caller at a time changes the data it contains. One disadvantage of functional global variables is that when you want to change the way you modify the resource they hold, you must change the functional global VI block diagram and add a new action. In some applications, where the use of global resources changes frequently, these changes might be inconvenient. In such cases, design the application to use a semaphore to protect access to the global resource.

A semaphore, also known as a Mutex, is an object you can use to protect access to shared resources. The code where the shared resources are accessed is called a critical section. In general, you want only one task at a time to have access to a critical section protected by a common semaphore. It is possible for semaphores to permit more than one task (up to a predefined limit) access to a critical section.

A semaphore remains in memory as long as the top-level VI with which it is associated is not idle. If the top-level VI becomes idle, LabVIEW clears the semaphore from memory. To prevent this, name the semaphore. LabVIEW clears a named semaphore from memory only when the top-level VI with which it is associated is closed.

Use the Create Semaphore VI to create a new semaphore. Use the Acquire Semaphore VI to acquire access to a semaphore. Use the Release Semaphore VI to release access to a semaphore. Use the Destroy Semaphore VI to destroy the specified semaphore.

The following illustration shows how you can use a semaphore to protect the critical sections. The semaphore was created by entering 1 in the size input of the Create Semaphore VI.

Each block diagram that wants to run a critical section must first call the Acquire Semaphore VI. If the semaphore is busy (its size is 0), the VI waits until the semaphore becomes available. When the Acquire Semaphore VI returns false for timed out, indicating that it acquired the semaphore, the block diagram starts executing the false case. When the block diagram finishes with its critical section (Sequence frame), the Release Semaphore VI releases the semaphore, permitting another waiting block diagram to resume execution.


Resources


 

Your Feedback! poor Poor  |  Excellent excellent   Yes No
 Document Quality? 
 Answered Your Question? 
Add Comments 1 2 3 4 5 submit