Friday, May 20, 2011

Multithreading with CVI - Part 1

Written by Tal Alon - May 2011

When a program is executed it is called a ‘process’. Our computer seems to run many such processes simultaneously, the Antivirus software, the web browser, file-manager, Word etc. are all running at the same time. You can activate the Task-Manager and click the ‘Processes’ tab to see the ones that are running on your computer now. This multitasking is an illusion made possible by the ability of the operating system (OS) to quickly (nanoseconds) switch between processes - suspending one process while running the others and then coming back to that process to continue its execution from where it left it. A computer with more than one CPU and the correct OS can actually perform several lines of code at the same time so that one process can run on one CPU while a second process runs on another, but the OS still has to do a lot of switching since there are more processes than CPUs.


A process can usually perform only one instruction at a time, so for example - while a for loop is executed the GUI timer will not call its callback function, and the rest of the program is stuck (even the GUI is not responsive) until the loop ends and the function exits. It is possible however to split a process into several ‘threads’ (see the sketch above) that will run simultaneously on different CPUs or be switched by the OS in a similar way to different processes. This is called Multithreading. We can then run a loop on a separate thread so that even while it iterates our GUI will stay responsive. We want to split a program into several ‘threads’ to allow it to perform more than one time-critical action at any given time, such as collecting data via a precise loop and showing the data graphically on the GUI. 

In this post we present a simple way to create and run code in a separate thread.

All the functions shown here and the rest of the multithreading functions are located in Library-->Utility-->Multithreading.

The Simplest Way to Create a New Thread

The idea is rather simple, you can write a function with the special prototype:

int CVICALLBACK ThreadFunction (void *functionData); 

And at any time you can instruct the program to run this function in a new and separate thread (while the main thread keeps running separately). The quickest and simplest way to do this in CVI is by using the function  ‘CmtScheduleThreadPoolFunction’. 

For example, If I create a thread function called 'Thread1' that looks like this (same prototype as before):

int CVICALLBACK Thread1 (void *functionData)
{
 int i;
 for (i=0 ; i<100 ; i++)
 {
  SetCtrlVal(panelHandle, PANEL_NUMERICTHERM_2, i);
  Delay(0.1);
 }
 return 0;
}

I can launch it in a new thread running this function simply by writing the following line:

CmtScheduleThreadPoolFunction (DEFAULT_THREAD_POOL_HANDLE, Thread1, NULL, NULL);

After this line our process is divided into two separate threads running in parallel. The second thread terminates when the function finishes whatever it is it's doing and returns. A graphical representation of this is shown at the top of this post.

The last two arguments of ‘CmtScheduleThreadPoolFunction’ can be used for more complex operations and you can read more about it in CVI's help files (also available online). 

How to Perform an Action Immediately as a Thread Exits

There are many ways to monitor the execution progress of a thread function. I will demonstrate one method which I find quite helpful. 

First define a global int variable that will hold the unique thread handle:

int ThreadID1=0;

Now instead of using  ‘CmtScheduleThreadPoolFunction’ use ‘CmtScheduleThreadPoolFunctionAdv’ like this:

CmtScheduleThreadPoolFunctionAdv (DEFAULT_THREAD_POOL_HANDLE,
                                                           Thread1,
                                                           NULL,
                                                           DEFAULT_THREAD_PRIORITY,
                                                           EndFunction,
                                                           EVENT_TP_THREAD_FUNCTION_END,
                                                           NULL,
                                                           CmtGetCurrentThreadID(),
                                                           &ThreadID1);

Where 'Thread1' is the name of the function we want to run in a new thread, 'EndFunction' is the function we want to execute when the 'Thread1' function returns and '&ThreadID1' is the address of the integer handle we declared globally before.

In this configuration the function 'EndFunction' will run automatically as soon as the thread function finshes and returns. And it will run in the thread in which ‘CmtScheduleThreadPoolFunctionAdv’ was called (usually in your case it will be the main thread). The 'EndFunction' should have a specific prototype that looks like this:

void CVICALLBACK LaunchThread2 (int poolHandle,
                                                               int functionID,
                                                               unsigned int event,
                                                               int value,
                                                               void *callbackData); 

The next post features another important aspect of multithreading - Locks. In it I discuss the way to protect variables and shared assets by using the following functions:

CmtNewLock
CmtDiscardLock 
CmtGetLock
CmtReleaseLock

You can read CVI's official multithreading tutorial here: tutorial, and if for some reason the page is not available, here is a link to the pdf: link.

16 comments:

  1. Hello,
    I just wanted to say that I really enjoyed your site and this post. You make some very informative points.
    Keep up the great work!

    ReplyDelete
  2. Thanks for taking the time to tell us that. Keep in touch.

    ReplyDelete
  3. Thanks for your information. It´s very useful.

    ReplyDelete
  4. This was great. Thanks!

    ReplyDelete
  5. Great work!! Thanks for your valuable information..

    ReplyDelete
  6. Thank you for the tutorials! Will there be future post on CVI?

    ReplyDelete
  7. Hopefully :) These take a lot of time.
    We have some post in the making.

    ReplyDelete
  8. May you give any example for coding in graphics mode?

    ReplyDelete
  9. Thank you very much !! I hope that you will post other tutorial soon !!

    ReplyDelete
  10. Very nice site.

    ReplyDelete
  11. Hi,
    Is it possible to stop the executing thread safely at any time? Lets say we have a thread function as follows ;

    int CVICALLBACK Thread1 (void *functionData)
    {
    int i;
    for (i=0 ; i<100000 ; i++)
    {
    SetCtrlVal(panelHandle, PANEL_NUMERICTHERM_2, i);
    Delay(1);
    }
    return 0;
    }



    It takes 100000 seconds to finish the thread. What want to to is stop the thread on pressing the Stop button, any where in the function

    Regards

    ReplyDelete
  12. Hi Cengiz,

    It is possible but you don't want to do that without the thread's permission :) if you just kill the thread from the outside it will stop suddenly (without freeing resources etc.) and that is not good. I recommend using a thread safe flag (with a thread lock) that the thread loop checks before every iteration and using it to request the thread to exit.

    The thread will see the flag and exit correctly.

    ReplyDelete
  13. Hello,

    I have a problem with killing second thread:
    I created a second thread... pushing the START button will be run while loop in second thread...
    I want to stop this second thread from default thread by pushing STOP button...
    The problem is that one cycle takes about 10 second and I do not wanna wait 10s, I want to stop immediately.
    I used CmtTerminateThreadPoolThread function but it freezes all program...
    Anyone another reason?

    Thank you for answers!

    the same problem as Cengiz

    Dear Tal alon can you explain how works your solution?

    Regards, Peter

    ReplyDelete
  14. A possible suggestion: If you wish to exit immediately, the loop within the thread must not use long ‘Delay’ or ‘Sleep’ intervals. You can place a small loop at the end of the thread main loop which will loop quickly and check the flag I mentioned in my previous answer here. This inner loop should keep going until it is time to begin another iteration of the main thread loop.

    ReplyDelete
  15. I want to use CVI multithreading technique in a CVI .dll function. Is it possible?

    ReplyDelete