01-05-2019 01:01 PM
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);
Solved! Go to Solution.
01-05-2019 01:20 PM
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
01-05-2019 06:33 PM
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.
01-07-2019 12:07 AM
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.
01-07-2019 12:21 AM
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.
01-07-2019 02:36 PM
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
01-07-2019 06:20 PM
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;
}
01-07-2019 06:28 PM
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);
01-08-2019 02:18 AM
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.
01-15-2019 08:17 PM
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.