LabWindows/CVI

cancel
Showing results for 
Search instead for 
Did you mean: 

Labwindows is not creating new thread from default thread pool instead waits for the the existing thread to be available

Solved!
Go to solution

My current application performs all the UI tasks in main thread and I am trying to create a separate thread for each module (temperature, encoders and force module) using CmtScheduleThreadPoolFunctionAdv   from default thread pool. However I noticed lab windows is creating only one thread for all the three calls. Also it is waiting for the secondary thread it created to be available to process all the modules.

 

CmtScheduleThreadPoolFunctionAdv (
                        DEFAULT_THREAD_POOL_HANDLE, DAQTempThreadFunction, &temperatureParams,
                        THREAD_PRIORITY_NORMAL, NULL, 0, NULL, 0,
                &daqTempThreadFunctionID);

 

CmtScheduleThreadPoolFunctionAdv (
                        DEFAULT_THREAD_POOL_HANDLE, DAQEncThreadFunction, &encoderParams,
                        THREAD_PRIORITY_NORMAL, NULL, 0, NULL, 0,
                &encThreadFunctionID);

 

CmtScheduleThreadPoolFunctionAdv (
                         DEFAULT_THREAD_POOL_HANDLE, DAQPressureThreadFunction, &pressureParams,
                         THREAD_PRIORITY_NORMAL, NULL, 0, NULL, 0,
                 &pressureThreadFunctionID);

0 Kudos
Message 1 of 13
(4,031 Views)

I can't find any information as to the values used for the default thread pool but it's very possible that it does only allow for one secondary thread.

 

You can check this yourself by querying the properties of the default thread pool with this:

 

 

CmtGetThreadPoolAttribute (DEFAULT_THREAD_POOL_HANDLE, attributeID, &attributeValue);

where attributeID is one of these values:

ATTR_TP_MAX_NUM_THREADS
ATTR_TP_NUM_THREADS
ATTR_TP_NUM_ACTIVE_THREADS

 

Rolf Kalbermatter
My Blog
Message 2 of 13
(4,030 Views)

According to the documentation

the maximum number or threads in the default thread pool is 2 + (2 * (number of processors))

that is, even with a single core processor the system should be able to create the threads.

There must be another reason for the unusual behavior, I'd like to know how Guru99 determines if a thread has been created or not.



Proud to use LW/CVI from 3.1 on.

My contributions to the Developer Community
________________________________________
If I have helped you, why not giving me a kudos?
Message 3 of 13
(4,010 Views)

 

 Hi Rolfk, Thanks for your reply. I have tried your suggestion and found application has properly created the thread pool (2 + 2 * numProcessors/cores). I have attached results from debugger for your reference. I am not sure why Labwindows is behaving like, but document says it creates a new thread. Not sure if there is any configuration required in this case.

 

0 Kudos
Message 4 of 13
(3,976 Views)

Hi Robert, Thanks for your response. Attached has the details about threadid's I observed using "CmtGetCurrentThreadId()" method in respective functions. Also default thread pool has allocated as per the formula you mentioned. I am not sure why Labwindows is behaving like this. I also raised a service request for this. Awaiting response.

0 Kudos
Message 5 of 13
(3,975 Views)

Hello!

When you checked values like the ATTR_TP_NUM_THREADS and ATTR_TP_NUM_ACTIVE_THREADS in your thread functions (or after creating the thread), what values were you getting? The help notes that if all threads in the pool are being used, the pool will execute the function once a thread is available, maybe CVI thinks all of it's threads are being used. 

I assume you are, but did you ensure that you're calling the CmtReleaseThreadPoolFunctionID in your program since you aren't passing NULL for the thread function ID?  

It's a bit difficult to understand the different pieces of your application (or a simplified example of it). your screenshot of your code has a function called "encoderReadCallback". When are you expecting this to execute? Do you want this as a callback to the DAQEncThreadFunction? Did you rename the original function that you mentioned in your first post? Knowing this can give us a better idea of what threads we should be expecting to execute. 

R Dahlman




Message 6 of 13
(3,952 Views)

Hi DahlmanR, Thanks for looking in to this. Please check my response below.

 

When you checked values like the ATTR_TP_NUM_THREADS and ATTR_TP_NUM_ACTIVE_THREADS in your thread functions (or after creating the thread), what values were you getting? The help notes that if all threads in the pool are being used, the pool will execute the function once a thread is available, maybe CVI thinks all of it's threads are being used. 

--[Guru] ATTR_TP_NUM_THREADS  = 1; ATTR_TP_NUM_ACTIVE_THREADS =0; ATTR_TP_THREAD_PRIORITY = 0 (Even after I tried to change to TIME_CRITICAL) and ATTR_TP_MAX_NUM_THREADS = 10 (2 +2 * numprocessors)

I assume you are, but did you ensure that you're calling the CmtReleaseThreadPoolFunctionID in your program since you aren't passing NULL for the thread function ID?  

 

--[Guru] Yes, I am releasingThreadPool once user selects stop measurement from UI. This has been verified through debugger. I don't see issue in releasing.

CmtReleaseThreadPoolFunctionID (DEFAULT_THREAD_POOL_HANDLE,
                                                daqTempThreadFunctionID);

 

It's a bit difficult to understand the different pieces of your application (or a simplified example of it). your screenshot of your code has a function called "encoderReadCallback". When are you expecting this to execute? Do you want this as a callback to the DAQEncThreadFunction? Did you rename the original function that you mentioned in your first post? Knowing this can give us a better idea of what threads we should be expecting to execute. 

--[Guru]This is a great question. I have  used DAQmxRegisterEveryNSamplesEvent to call whenever there is a vaue in the buffer and call encoderReadCallBack() method to retrieve value. Code below.

 

 

DAQmxErrChk (DAQmxCfgSampClkTiming(gEncTaskHandle,"",encoderParamsPtr->encRate,DAQmx_Val_Rising,DAQmx_Val_ContSamps,1000));
 DAQmxErrChk (DAQmxGetTaskAttribute(gEncTaskHandle,DAQmx_Task_NumChans,&gNumEncChannels));
 
 Fmt (daqMesg, "Encoder Strip Chart Channels: %d", gNumEncChannels);
 PostDeferredCallToThreadAndWait (encoderTabCB, daqMesg, mainThreadID, POST_CALL_WAIT_TIMEOUT_INFINITE);
 
 DAQmxErrChk (DAQmxRegisterEveryNSamplesEvent(gEncTaskHandle,DAQmx_Val_Acquired_Into_Buffer,ENCODER_ANALYSIS_LENGTH,DAQmx_Val_SynchronousEventCallbacks,encoderReadCallback,NULL));
 DAQmxErrChk (DAQmxRegisterDoneEvent(gEncTaskHandle,DAQmx_Val_SynchronousEventCallbacks,DoneEncoderCallback,NULL));
  
 if( (gData=malloc(gEncSamplesToRead * gNumEncChannels * sizeof(float64)))==NULL ) {
  MessagePopup("Error","Not enough memory");
  goto Error;
 }

 /*********************************************/
 // DAQmx Start Code
 /*********************************************/
 if(!encoderTaskStarted){
  DAQmxErrChk (DAQmxStartTask(gEncTaskHandle));
  encoderTaskStarted = 1;
 }
 

 

0 Kudos
Message 7 of 13
(3,943 Views)

All, I have an update to my Initial question. I tried adding a while loop with ProcessSystemEvents() function in the three thread functions after task is created and then labwindows started creating/allocating new thread from the default thread pool as required (new thread, required priority etc.). I was under assumption that DAQmxRegisterEveryNSamplesEvent is enough to keep the thread active, but looks we must have a while loop and should not return. This is taking too much CPU though. Code snippet below. If anyone have better idea in keeping thread active please me know.

 

 

 DAQmxErrChk (DAQmxRegisterEveryNSamplesEvent(gPressureTaskHandle,DAQmx_Val_Acquired_Into_Buffer,pressureParamsPtr->samples,
             DAQmx_Val_SynchronousEventCallbacks,pressureReadCallback,NULL));
 DAQmxErrChk (DAQmxRegisterDoneEvent(gPressureTaskHandle,DAQmx_Val_SynchronousEventCallbacks,DonePressureCallback,NULL));
 
 if( (gData=malloc(pressureParamsPtr->samples*gNumPressChannels*sizeof(float64)))==NULL ) {
  MessagePopup("Error","Not enough memory");
  goto Error;
 }
 
 /*********************************************/
 // DAQmx Start Code
 /*********************************************/
 if(!pressureTaskStarted){
  DAQmxErrChk (DAQmxStartTask(gPressureTaskHandle));
  pressureTaskStarted = 1;
 }
 
 unsigned char dimmed = 1;
 Fmt (daqMesg, "Pressure Start Dimmed On: %d", dimmed);
    PostDeferredCallToThreadAndWait (PressureTabCB, daqMesg, mainThreadID, POST_CALL_WAIT_TIMEOUT_INFINITE);
 
 /* Process user-interface, TCP and other system events. */
 while (!pressEventStopFlag)
 {
  ProcessSystemEvents ();
 }
 
 CmtReleaseLock(pressThreadLock);

0 Kudos
Message 8 of 13
(3,940 Views)

I understand a simple endless loop consumes too much CPU, for this reason I developed a slightly different approach that minimizes CPU usage leveraging the event paradigm.

 

The concept is to use a Win32 event to sync the process: set the event in EveryNSamplesCallback and wait for it in the thread loop, where you can reading and process data when the event is set. This approach permits to minimize the impact of threads on CPU: I have applications developed on this framework with concurrent threads that handle DAQmx acquisition as well as communication over RS232 and TCP with external devices.



Proud to use LW/CVI from 3.1 on.

My contributions to the Developer Community
________________________________________
If I have helped you, why not giving me a kudos?
0 Kudos
Message 9 of 13
(3,933 Views)
Solution
Accepted by topic author Guru99

Thanks for the reply. I looked at the event approach and also

I was able to minimize CPU spikes using SetSleepPolicy (VAL_SLEEP_SOME) for each thread. I am considering a while loop with processSystemEvents and right sleep policy is good enough for now.

0 Kudos
Message 10 of 13
(3,777 Views)