LabWindows/CVI

cancel
Showing results for 
Search instead for 
Did you mean: 

Processing events while waiting for a thread to complete

Another threading question...

I start a function in another thread when a button on main panel is clicked,
then I wait for this thread to finish, like:

CmtScheduleThreadPoolFunction(DEFAULT_THREAD_POOL_HANDLE,
RunTestThreadFunction, NULL,
&functionId);
// Wait for test to complete
CmtWaitForThreadPoolFunctionCompletion(DEFAULT_THREAD_POOL_HANDLE,
functionId, OPT_TP_PROCESS_EVENTS_WHILE_WAITING);

int CVICALLBACK RunTestThreadFunction(void* pFunctionData)
{
Wait(10.0);
return 0;
}

I am using OPT_TP_PROCESS_EVENTS_WHILE_WAITING while waiting for the thread
to complete, but while waiting, I can not click on any other button or menu
on my main panel. I can move the main panel during the same
time. Any ideas?


vishi
0 Kudos
Message 1 of 10
(4,666 Views)
Did you create or load the panel in the same thread as is making the CmtScheduleThreadPoolFunction() call? My understanding of CVI multi-threading is that events must be processed by the same thread as has created or loaded the panel in order for the controls to work (i.e. invoke a callback). If this is not the case in your application, it could explain the inoperability of the main panel controls despite the apparent processing of events while waiting for the thread function to complete.
0 Kudos
Message 2 of 10
(4,666 Views)
That is correct; you have to process user interface events in the same thread that created the top-level panel. You can refer to the KnowledgeBase below for more information.

http://pong.ni.com/public.nsf/websearch/B723EA540E434B26862565FC007BD404?OpenDocument

Mika Fukuchi
Application Engineer
National Instruments
0 Kudos
Message 3 of 10
(4,666 Views)
Vishi,

The reason you are seeing this is because there is a difference between CVI UI events and operating system events. The OPT_TP_PROCESS_EVENTS_WHILE_WAITING option allows you to process operating system events, but not UI events. This is why your UI is blocking.

I realize that this distinction is not very obvious, and therefore in the next version of CVI we are changing the behavior of OPT_TP_PROCESS_EVENTS_WHILE_WAITING to also process UI events.

In the meanwhile, whenever the wait for a thread to complete is non-negligible, you should wait in the following manner:

threadCompleted = FALSE;
CmtScheduleThreadPoolFunction(DEFAULT_THREAD_POOL_ HANDLE,
RunTestThreadFunction, NULL,
&functionId);
// Wait for test to complete

while (!threadCompleted)
ProcessSystemEvents();
CmtWaitForThreadPoolFunctionCompletion(DEFAULT_THR EAD_POOL_HANDLE,
functionId, OPT_TP_PROCESS_EVENTS_WHILE_WAITING);


int CVICALLBACK RunTestThreadFunction(void* pFunctionData)
{
Wait(10.0);
threadCompleted = TRUE;
return 0;
}

The call to ProcessSystemEvents() will process both types of events.

Notice that I still left the CmtWaitForThreadPoolFunctionCompletion call. The reason for that is that there could be a small amount of time after the thread function has set the flag but before the thread has really exited. The additional wait will ensure that the thread has really completed. In this case, the wait time will be short enough that it should not have any effect on your UI.

Luis Gomes
NI
Message 4 of 10
(4,666 Views)
This is not a particularly good solution - you are spin-locking on the while (!threadCompleted) statement, consuming gobs of cpu cycles simply on the chance that the user will cause an event that requires processing. If the function you're waiting on is compute intensive, and if the function's thread is at the same priority as the waiting thread, you'll be spending half of the process's execution time simply checking on a possible event. You may want to introduce some delay into the while loop checking on the thread completion, which will allow the thread function to complete more quickly and still allow for GUI responsiveness. I've found that 50 or even 100 ms of delay is generally not noticeable to the user. You can use Win32 API Sleep() or Delay() to do
this. It's not clear what the NI routine Delay() does: is it merely a wrapper for Sleep(), or does it process events?

Spin-locking is the #1 no-no when multi-threading.

NI has not clarified its handling of events - to be fair, they are attempting to simplify the native Win32 event model so that it's easier to create applications, which is a good thing. But, it's hard to write a meaningful multi-threaded application without dealing with user interface events at a level that NI does not provide.

The native Win32 API provides MsgWaitForMultipleObjects() function to solve this problem. You can wait for an object (such as a thread) to become signaled (completed) while still being able to process user interface events (should they occur) without burning up lots of CPU cycles.
0 Kudos
Message 5 of 10
(4,666 Views)
Thanks Luis,

I found another way, where I use CmtSchedule Advance function and register a
callback which gets called when my thread is finished. So when user clicks a
button on main panel, I start my thread, and exit the button's callback
function (rather than just sitting there with CmtWait function). This way I
can keep processing UIR events on main panel.


vishi

"Luis Gomes" wrote in message
news:5065000000050000007EC10000-1031838699000@exchange.ni.com...
> Vishi,
>
> The reason you are seeing this is because there is a difference
> between CVI UI events and operating system events. The
> OPT_TP_PROCESS_EVENTS_WHILE_WAITING option allows you to process
> operating system events, but not UI events. This is why your UI is
> blocking.
>
> I realize that this distinction is not very obvious, and therefore in
> the next version of CVI we are changing the behavior of
> OPT_TP_PROCESS_EVENTS_WHILE_WAITING to also process UI events.
>
> In the meanwhile, whenever the wait for a thread to complete is
> non-negligible, you should wait in the following manner:
>
> threadCompleted = FALSE;
> CmtScheduleThreadPoolFunction(DEFAULT_THREAD_POOL_ HANDLE,
> RunTestThreadFunction, NULL,
> &functionId);
> // Wait for test to complete
> while (!threadCompleted)
> ProcessSystemEvents();
> CmtWaitForThreadPoolFunctionCompletion(DEFAULT_THR EAD_POOL_HANDLE,
> functionId, OPT_TP_PROCESS_EVENTS_WHILE_WAITING);
>
>
> int CVICALLBACK RunTestThreadFunction(void* pFunctionData)
> {
> Wait(10.0);
> threadCompleted = TRUE;
> return 0;
> }
>
> The call to ProcessSystemEvents() will process both types of events.
>
> Notice that I still left the CmtWaitForThreadPoolFunctionCompletion
> call. The reason for that is that there could be a small amount of
> time after the thread function has set the flag but before the thread
> has really exited. The additional wait will ensure that the thread has
> really completed. In this case, the wait time will be short enough
> that it should not have any effect on your UI.
>
> Luis Gomes
> NI
0 Kudos
Message 6 of 10
(4,666 Views)
While I don't disagree with any of the points you're making, I do want to point out that the ProcessSystemEvents function is not exactly spin-locking. Built into that function is a call to the Win32 Sleep() (depending on your SetSleepPolicy choice), and while it does consume more CPU cycles than MsgWaitForMultipleObjects would, it readily yields them whenever something else in your system causes the CPU activity to ramp up.

The problem with MsgWaitForMultipleObjects is that it would not fix Vishi's problem, since that function cannot unlock the CVI UI objects (since it doesn't know about them).

- luis
0 Kudos
Message 7 of 10
(4,666 Views)
It's hard to figure out just what ProcessSystemEvents() does from the NI documentation: the info on the function itself makes no mention of suspending. You have to stumble upon the info on SetSleepPolicy() to infer that a suspension may occur when using this function.

If you're privy to NI source code, I'm curious how Delay() is implemented, does it process any CVI UI events, does it simply call Sleep(), or does it spinlock?

I wasn't proposing MsgWaitForMultipleObjects() as a solution to this problem: I was attempting to point out that the Win32 API provides a solution for this particular problem when you're using the native UI and message queues.
0 Kudos
Message 8 of 10
(4,666 Views)
I don't think Delay processes any events, that's why I use CVI timers to
create my own delay like:

t0 = Timer();
t1=Timer();

while ((t1-t0) < Delay) {
ProcessSystemEvents();
t1 = Timer();
}

Havne't used Sleep in CVI. Last time tried the online MSDN help, it started
talking all that .NET crap, so gave up.

vishi

"menchar" wrote in message
news:506500000005000000C3C10000-1031838699000@exchange.ni.com...
> It's hard to figure out just what ProcessSystemEvents() does from the
> NI documentation: the info on the function itself makes no mention of
> suspending. You have to stumble upon the info on SetSleepPolicy() to
> infer that a suspension may occur when using this function.
>
> If you're privy to NI sourc
e code, I'm curious how Delay() is
> implemented, does it process any CVI UI events, does it simply call
> Sleep(), or does it spinlock?
>
> I wasn't proposing MsgWaitForMultipleObjects() as a solution to this
> problem: I was attempting to point out that the Win32 API provides a
> solution for this particular problem when you're using the native UI
> and message queues.
0 Kudos
Message 9 of 10
(4,666 Views)
Yes, the Delay function sleeps. It does not spinlock.

But it also does not process UI events. The reason why it does not process UI events is the same reason why the CmtWaitForThreadPoolFunctionCompletion function did not process UI events. We try to keep our libraries (in this case the UI library and the Utility library) independent of each other.

- luis
0 Kudos
Message 10 of 10
(4,666 Views)