Saturday, May 21, 2011

Multithreading with CVI - Part 2


Written by Tal Alon - May 2011

Following the previous multithreading post, here we shall discuss the important topic of 'locks':

Whenever you are multithreading, you must take special care of the assets that are being shared by more than one thread. Global variables, static local variables, dynamically allocated variables and the access to 3rd party libraries (DLLs) must all be protected.

A shared asset (a variable, for example) is considered protected if there is some mechanism that prevents more than one thread from accessing it at any given time. Failure to protect such assets can cause logical bugs that are extremely hard to detect (and you might not even notice them at first).

What we actually need is a 'lock', and in this post we explain how to define and use one. 

You might initially think that all we need is a global integer that can be used as a flag to signify if an asset is being used right now: if a thread wishes to access the asset it first checks the value of the flag, if it is zero it means it is not being used and the thread changes its value to one, uses it and when it is done with it changes its value back to zero. If the thread wishes to access the asset and the flag's value is one (it is being used by another thread) it loops until the value change back to zero and then changes its value to one, access it and when it is done with it changes its value back to zero. This is very similar to the way actual locks work, but the problem here is that the global integer flag itself is a shared asset... so we must protect it too.

A real lock is indeed a global integer that is used as a flag, but to make it a real lock you must use special functions that utilizes operating system functions to access that integer - to check its value and change it in a thread-safe way:

First define a global integer that will be used to hold the state of the lock:

int lock1;

Then, in the main function (before 'RunUserInterface' and before any new thread is launched), make this integer into a real lock by using the function 'CmtNewLock':

CmtNewLock (NULL, 0, &lock1);

This lock will have to be released when you are done with it (a possible place is right after 'RunUserInterface' in main). This can be done by using the function 'CmtDiscardLock':

CmtDiscardLock (lock1);

Now, whenever you want to check if another thread is using the asset associated with the lock 'lock1' you use 'CmtGetLock':

CmtGetLock (lock1);

This function waits (will not go over to the next line of code) until the lock is free and then automatically 'locks' it again (signifying that the asset is now in use). This process is called 'getting a lock'.

To release the lock so that other threads can access the asset, use the function 'CmtReleaseLock':

CmtReleaseLock (lock1);

It is important to remember that the lock doesn't actually restricts the access to the asset. It is up to you to get a lock before accessing the asset, and it is up to you to release the lock when the asset is no longer being accessed.



A good metaphor for this, although a little uneasy one, is a public toilet with a broken lock. The janitor places a sign near the toilet that can say 'Occupied' or 'Vacant' (free). This sign is a metaphor for our multithreading lock, and the toilet itself is a metaphor for the asset.

When you want to use the toilet you can obviously just barge in, regardless of the sign, and this can sometimes result in a unpleasant incident - this will happen to you if you do not use CmtGetLock before using an asset. It is important to wait until it says 'Vacant', change it to 'Occupied' before we go in (these two actions are performed by CmtGetLock)... and when we are done (...ahm...) go out and change it back to 'Vacant' (this is done using CmtReleaseLock), and then of course wash our hands.

When we are already inside the toilet we might act like real jerks and exit through the window... leaving the sign outside 'Occupied'... and the sign-respecting people that are waiting outside screwed (they will never ever get to have their way) - You'll do just that if you forget to release the lock with CmtReleaseLock don't!

To read more about multithreading and locks in CVI read the full tutorial here.

2 comments:

  1. Although already a few years old, still a great tutorial!

    ReplyDelete
  2. Great tutorial, easy to understand. Thanks so much.

    ReplyDelete