General Information on Multithreading for C Programmers
Primary Software: LabWindows/CVI Development Systems>>Full Development System
Primary Software Version: 5.5
Primary Software Fixed Version: N/A
Secondary Software: N/A
Problem: I have questions on how to protect data, assign tasks to processors, improve performance with multithreading, and other general information on multithreading. Do you have any information on these topics?
Solution: There is an extremely useful document at the end of this brief overview that goes into the details of multithreading with examples, code snippets, and thorough explanations. This brief description is basically a summary of that document and is specific as of CVI 5.5 and newer, but the information is useful for multithreading in general.
There are four main reasons to add more than one thread (i.e. "multithreading") to an application.
- To execute multiple tasks without them interfering with each other. A common example is doing data acquisition and displaying user-interface data. The user interface could slow the data acquisition down if the user starts moving the window or clicking buttons if there's only one thread, but if there is a thread for the user interface operations and a thread dedicated solely to data acquisition, the user interface operations won't interfere with data acquisition.
- Perform slow input/output operations simultaneously. Communicating to instruments or writing/reading from the hard drive takes time and if there is only one thread, the program must wait for one task to finish before it steps to the next. With multithreading, one could be doing file I/O while communicating with instruments to save time.
- Improve performance on a multiprocessor machine. Assigning certain threads to certain processors may enable the user to improve efficiency so one task can actually be executed at the same time as another, like file I/O and data acquisition.
- Needing to perform a task in more than one context at the same time. A good example of this is running a server that needs to respond to all it's client's messages. With only one thread, the program would need to respond to every client and if some task from a particular client was taking up large amounts of time, other messages from clients would not get answered in a timely manner if the program did not correct for this. With multiple threads, each client can have their own thread, so the program doesn't need to worry about checking on other clients because they will automatically get handled by their own thread when they have a message.
Operating System Considerations: Windows 2000, NT, XP and Vista support multiprocessor machines. On single processor machines XP and Vista handle thread switching more efficiently than 2000 and NT.
CVI and Multithreading - The rest of this document pertains to CVI 5.5 and how it handles multithreading.
CVI 5.5 has incorporated SDK functions into the environment to provide easy to use CVI functions for creating multithreaded applications. These high level functions are part of the CVI built in library. CVI 5.5 also supports debugging of multithread applications. Below is a list of different mechanisms CVI uses to handle multithreading and protecting data from multiple threads accessing it at the same time.
- The thread pool is a simple way to easily create multiple threads. This feature allows the user to create a pool of threads that can be assigned to different tasks by associating different threads from the pool with different functions.
- An asynchronous timer is a good way to execute a particular function at regular intervals while doing other tasks at the same time. The asynchronous timer functions are in a function panel (asynctmr.fp) that must be loaded with the project. The asynchronous timer uses the Windows multimedia timer which has a minimum interval of about 10ms (depending on your system) and handles all the asynchronous calls with one thread. For this reason, it is advised to only use one asynchronous timer. If more are needed, use the thread pool.
Mechanisms to
PROTECT data
- Thread Locks allow threads to obtain a lock for a particular block of code and then the thread releases the lock when done with the code. Only one thread is allowed to obtain a lock for a specified block of code at a time. This is useful for manipulating several variables at once, so protecting each piece of data isn't necessary. This can take longer and be more inefficient by making other threads wait until the thread with the lock finishes with the block of memory, but it is less error prone than protecting each variable individually. Thread locking would also be good for protecting access to third party DLLs that are not thread safe. A final use for thread locks would be protecting shared memory between programs that might try to use it at the same time and interfere with each other.
- A Thread Safe Variable is another method to protect data. This combines the operating system's thread lock and the data into one entity. Thread Safe Variables help eliminate common errors of forgetting to get a lock before accessing data or releasing the lock afterwards. It also makes passing the data to functions simplified because instead of needing the thread lock handle and the data, just need to pass the thread safe variable. Accessing these variables involves macros, which are functions that have the variables name as part of the functions name. Thread Safe Variables can be used for any data type like structures, arrays, strings, but it is up to the programmer to keep track of the length of arrays or strings every time elements are added or removed.
- Thread Safe Queues allows CVI to safely pass data between threads. All the locking of the data is done internally. A common application would be acquiring data in a thread safe queue and having another thread read from that Thread Safe Queue when data is available. The Thread Safe Queues maintain the length, so you don't have to keep track of how long your array has grown.
- Thread Local Variables are useful for maintaining a separate value for each thread. This would be useful for spawning new threads to test UUT (units under test) and having each thread save a Thread Local Variable of the serial number and voltage for each UUT.
Deadlocks occur when two threads are waiting on each other to continue. A example would be, Thread 1 has a lock on code A and needs to execute code B before releasing the lock. Thread 2 has a lock on code B and needs to execute code A before releasing the lock. A way to avoid deadlocks when acquiring more than one thread locking object, is every thread in the program must always acquire thread locking objects in the same order and release them in reverse order.
An important feature of multithreading is assigning priorities to threads. There are different levels of priority from time critical to idle. It is not recommended to assign threads to time critical priority unless it is for short periods of time because they ignore system interrupts such as mouse, keyboard, hard disk and other critical systems which could cause the program to crash or hang if not serviced appropriately. Be careful when changing the default thread priority.
The last topic applies to multiprocessor systems. Assigning certain threads to particular processors is something that must be done through SDK functions, it's NOT in the CVI libraries. The reasons this functionality isn't included is because it's a very simple SDK call that CVI could not have made any simpler. The SDK function
SetThreadIdealProcessor (ThreadId, ProcessNum) can be called to assign ThreadId to a zero based ProcessNum. The ThreadID can be obtained from a CVI call and the number of processors on the machine can be determined from a CVI call to
CmtGetNumberOfProcessors. Keep in mind that Windows NT, 2000, XP and Vista support multiprocessor systems. Earlier versions of Windows do not support multiprocessor systems.
More detailed information on all these topics and some additional topics are included in the PDF below. This document is also available in the LabWindows/CVI bookshelf under
Applications and White Papers. You can get to the document by launching LabWindows/CVI and going to
Help » LabWindows/CVI Bookshelf.
Related Links: Developer Zone Tutorial: LabWindows/CVI MultithreadingAdditional Multithreading Questions
Attachments:
Report Date: 03/01/2000
Last Updated: 02/19/2008
Document ID: 1V0DMG8M