A modern UI should avoid showing wait cursors to the user, and where necessary perform time consuming activities such as computation in a worker thread. The main thread should also tend to avoid performing I/O - particularly if it involves communication over a network. Otherwise the UI can feel unresponsive or sluggish to the user. Even blocking the main thread for just 50 milliseconds can impact the rate at which data entry can be performed by users.
It is useful for application programs to have a means to allow any thread to post a lambda function to a queue which is executed some time in the future by the main UI thread. This might have the following signature:
void PostTaskToMainUI( std::function<void()> task );
For example, a Windows application could implement this function in terms of the Win32 function
PostMessageW
, perhaps using WM_USER
for the Msg parameter:
BOOL PostMessageW(
[in, optional] HWND hWnd,
[in] UINT Msg,
[in] WPARAM wParam,
[in] LPARAM lParam
);
Consider that there is a function available to any thread to post tasks to a worker thread (or a thread pool):
void PostTaskToWorkerThread( std::function<void()> task );
This may cause the posted tasks to be queued.
Let there be a pure function which calculates an output for a given input:
Output CalculateOutput(Input input);
Consider the following event handler which is called by the main UI thread and which retrieves the input
(perhaps from a model in an MVC design), directly calls CalculateOutput
then updates the UI
(perhaps by updating derived variables in a model):
// Synchronous version which might make the UI feel unresponsive
void MyUI::OnMouseButtonUp()
{
Input input = CalculateInput();
Output output = CalculateOutput(input);
UpdateUI(output);
}
Let's assume CalculateOutput
takes a long time, so that we cannot afford to call it
from the main UI thread.
Fortunately, since this is a pure function which doesn't access any global state, it can safely be called
by a worker thread.
Furthermore it is assumed the types Input
and Output
are value types
supporting default construction, copy and move construction and copy and move assignment.
// Asynchronous version which makes the UI very responsive
void MyUI::OnMouseButtonUp()
{
Input input = CalculateInput();
PostTaskToWorkerThread(
[input]()
{
Output output = CalculateOutput(input);
PostTaskToMainUI(
[output]()
{
UpdateUI(output);
});
});
}
Note the following:
OnMouseButtonUp
executes extremely quickly (perhaps in a few microseconds)
CalculateOutput
function.