I would like to use a ppl task to do some work in the background, and, upon completion, show the result in a window. In my case the UI-framework is MFC. The structure would be:
using namespace concurrency;
create_task([] {
// this can be run in any thread, shouldn't be the UI thread
// do real work here
return 42;
}).then([](int n)
{
// this should be run on the UI thread
// ... open a MFC window to display results
});
The thing is, a non Windows Store app doesn't allow to specify the task_continuation_context. Instead, the runtime decides which context will be used (see task_continuation_context Class).
Can I rely on the runtime to reliably figure out that it needs to run the continuation on the UI thread? Is there a reasonable workaround to achieve what I want - without blocking the UI thread?
Update: Playing around showed that the runtime will not run the continuation on the UI thread. So, is it impossible?
What I did to solve this was to create a Dispatcher class and a message-only window, which is a member of the Dispatcher. The Dispatcher must be constructed (I used a singleton) from the main thread such that the main thread takes care of the messages which are sent to the message-only window. I can pass a std::function to my Dispatcher::ExecuteInMainThread function. The Dispatcher will then invoke SendMessage on the message-only window, passing in the pointer (unfortunately only a pointer will be possible) to the std::function. The only message handler I need in the message-only window will then invoke the function I passed in - within the main thread.
This Dispatcher can be used in the task continuation to execute a std::function in the main thread.
Related
Does the Window Procedure specified as lpfnWndProc by window class during registration runs in a separate thread ?
There is an important concept in windows called the message loop.
It is usually inside the main function (aka: WinMain) and can be characterized in the following manner:
while (true) {
// blocks until there's a new message to process
GetMessage()
TranslateMessage()
// ends up calling the propper WndProc callback
DispatchMessage()
}
Update: When you create a window, the thread on which the window is created owns the windows (and the its message queue). Therefor, it must provide the message loop process. This is usually done in the application's main thread but, as other user stated, it can also be done in a separate thread.
The function DispatchMessage takes care of executing the WindowProc procedure of the window targeted by the message (as specified by the message's hwnd parameter).
So, when you create a window, the lpfnWndProc parameter specifies where you want to be notified for events (mouse clicks, keyboard presses, etc). And it is always called in the same thread (the application's main thread or the one which owns the window).
A word of advice: If you need to perform a potentially long operation as the result of an event, you must create a new thread (aka background worker) for the task, and perform some kind of IPC to notify the main thread when the function is finished.
You can find instructions about how to write a windows procedure here. Also, there is some info about the main loop in this wikipedia page.
Does the Window Procedure specified as lpfnWndProc by window class during registration runs in a separate thread ?
No, it is called (as a callback) when events (aka messages) are dispatched by your message loop. In this way - the so-called 'event-driven' model - your program is able to react to user input as and when it happens without having to deal with any multi-threading or re-entrancy issues.
You might have more than one thread, but if it has windows associated with it (i.e. CreateWindowEx was called by that thread) then it would need to have its own message loop.
I have a large MFC based application that includes some potentially very slow tasks in the main thread. This can give the appearance that the application has hung when it is actually working its way through a long task. From a usability point of view, I'd like to be giving the user some more feedback on progress, and have an option to abort the task in a clean manner. While hiving the long tasks off into separate threads would be a better long term solution, I'm thinking a pragmatic short term solution is create a new GUI thread encapsulated in its own object complete with dialog including progress bar and cancel button, used in a similar manner to a CWait object. The main thread monitors the cancel status via an IsCancelled method, and finishes via a throw when required.
Is this a reasonable approach, and if so is there some MFC code out there already that I can use, or should I roll my own? First sketch looks like this
class CProgressThread : public CWinThread
{
public:
CProgressThread(int ProgressMax);
~CProgressThread()
void SetProgress(int Progress);
BOOL IsCancelled();
private:
CProgressDialog *theDialog;
}
void MySlowTask()
{
CProgressThread PT(MaxProgress);
try
{
{
{ // deep in the depths of my slow task
PT.SetProgress(Progress);
if (PT.IsCancelled())
throw new CUserHasHadEnough;
}
}
}
catch (CUserHasHadEnough *pUserHasHadEnough)
{
// Clean-up
}
}
As a rule, I tend to have one GUI thread and many worker threads, but this approach could possibly save me a bunch of refactoring and testing. Any serious potential pitfalls?
Short answer, Yes, you can have multiple GUI thread in MFC. But you can't access the GUI component directly other than the created thread. The reason is because the Win32 under the MFC stores the GUI handler per thread based. It means the handler in one thread isn't visible to another thread. If you jump to the CWinThread class source code, you can find a handler map attribute there.
Windows (MFC) doesn't has hard difference between the worker thread & GUI thread. Any thread can be changed to GUI thread once they create the message queue, which is created after the first call related to the message, such as GetMessage().
In your above code, if the progress bar is created in one thread and MySlowWork() is called in another thread. You can only use the CProgressThread attributes without touch the Win32 GUI related functions, such as close, setText, SetProgress... since they all need the GUI handler. If you do call those function, the error will be can't find the specified window since that handler isn't in the thread handler mapping.
If you do need change the GUI, you need send the message to that progress bar owner thread. Let that thread handles the message by itself (message handler) through the PostThreadMessage, refer to MSDN for detail.
I have a thread that I pass an HWND of a window where it post messages and updates a progress bar. The user can however go to another window (modal) where I would also like to show the progress of this thread. Passing the initial HWND is obviously straight forward but how can I divert the thread to post messages to the new window once its running? This is a c++ worker thread. I am using Windows 7,MFC, C++.
Put it other way around. Let progress windows get the address of the class from worker thread, and ask it periodically for progress. With timer, for example. That way, you will be able to have as much progress windows as you need.
There are two approaches. One is Daniel's way, that polls progress info from the UI window. All you need is a thread-safe 'int GetProgressPercent() const' member in the thread-implementing class (or equivalent free function).
The other approach is to use PostMessage or PostThreadMessage in the worker thread as progress happens and on the other end process the message adjusting the bar.
The main problem with either is to handle lifetime issues, prevent calls to object or HWND when it is actually gone already. For that the PostThreadMessage way is probably the safest baseline, as the main thread is supposed to be there and manage all the other threads -- and track the overall state of the program able to do meaningful dispatch to live windows or nothing. But it's the most complex too.
I have C++ library (Win32 Console) where I have implemented an asynchronous function using a timer. The asynchronous method returns device information.
I have created a separate thread, "Timer Thread", to create a hidden window and then I call SetTimer() and then the implemented message loop.
When the timer expires, it enables a callback.
When I use the library in a console application, it is working fine.
In an MFC application I am doing a post message to update the user interface when the callback triggers. The post message is not working.
If I remove the message loop in the library, it is working fine in the MFC application.
I came to conclusion that:
I guess the problem is due to two message loops, one MFC (main thread) and the TimerThread message loop. So when the callback is called and the subsequent PostMessage results in the TimerThread message loop and not reported in the MFC (main thread) message loop.
If I remove the TimerThread message loop then it works fine in the MFC application, but it fails to work in the console application.
How do I overcome this problem?
class IDeviceEnumerationCallback
{
public:
virtual void onDeviceDiscovered(DeviceInfo* pDeviceInfo,unsigned short nNoOfDevice) = 0;
};
class IDeviceDiscovery
{
public:
virtual int InitialiseDiscovery(IDeviceEnumerationCallback*) = 0;
virtual void UnInitialiseDiscovery() = 0;
virtual int EnumerateDevice() = 0;
};
class CDeviceDiscovery:IDeviceDiscovery
{
//Implementation
}
In the MFC/console application I am implementing IDeviceEnumerationCallback to get a callback.
I am using Bonjour API to enumerate the device and all the methods in Bonjour API are callbacks.
I am wating for some time interval to enumerate a device using Bonjour API and then say after 400 ms I am calling a callback to return the result. In the MFC application when the callback is called, I am doing a PostMessage() to update the user interface.
Earlier I tried without Windows message pump. I had a SetTimer function, and it is working with the MFC application, but for the console application, the callback never gets called, so I implemented a message pump here. Now it is not working for MFC application.
First, there's no reason to do what you did: creating a separate threads, then creating a window in it, set the window timer, run the message loop, respond to the WM_TIMER message and invoke the callback.
If you create "your own" thread - you don't actually need all this. You could just implement a simple loop with either Sleep (or WaitForXXXX if you want an abort option), and invoke your callback.
Usually one creates a hidden window with a timer to avoid creating an additional thread. That is, within a thread that operates GUI (and hence - runs the message loop) you create a window, and it will be served by the message loop. Actually this is would you could do in your MFC app.
However, as you said, you want a generic code for both MFC and console apps.
In MFC application I am doing post message to update UI when the
callback triggers.the post message is Not wokring.
What exactly do you mean by "diung post message"? The message should be posted either to a specific window, or to the thread. In the first case it's dispatched to the window procedure, and in the second case the message loop implementation is responsible for handling the message.
If you post your message to a specific window - how do you get its handle (HWND)? Is it your app's main window (AfxGetMainWnd)? What does your thread start working, after the MFC has created the main window, or earlier?
I ask all those question because you seem to be a newbie (no offences), and those are typical mistakes.
The problem is that you should not be creating a hidden window and using SetTimer instead you should be using the MFC worker thread functionality for background work.
//You create a thread like so.
// you need a CMyObject only if you need to pass any information
//to the thread function.
CMyObject *pNewObject = new CMyObject;
AfxBeginThread(MyThreadProc, pNewObject);
//This function will be run in separate thread
UINT MyThreadProc( LPVOID pParam )
{
//The parameter that was passed to this function
CMyObject* pObject = (CMyObject*)pParam;
while( 1 )
{
//add your code to do stuff.
Sleep(5000); //or whatever your SetTimer interval was
}
return 0; // thread completed successfully
}
In Single Document Interface (SDI) or Multiple Document Interface (MDI) MFC application, I created an application wide timer in the View. The timer will tick as long as the application is running and trigger some periodic actions.
How can I do the same with Dialog Based MFC application?
Should I create Thread's Timer (SetTimer with NULL HWND) and pass a callback function to it?
Should I create worker threads? My experience with other projects was when I tried to display some feedback GUI from non-GUI/worker threads, I need to roll out my own "delegate"/command pattern and a "delegate invoker"/command invoker. The worker thread will send message (I think using message is safer than direct function call when dealing across thread-boundary, CMIIW) to the UI-thread. and the UI-thread will be the "delegate"/command invoker. Failing to do this and to make sure that the windows/dialogs have the correct parent will result in bizzare behaviors such as the Application suddenly disappears to the background; Window/Dialog that is shown behind the current window/dialog and causing the current window to be unresponsive/unclickable. Probably I was doing something wrong but there were so much problems when dealing with threads.
Are there best practices for this?
A timer works as well in a dialog-based application as an SDI or MDI app. OTOH, timers are (mostly) a leftover from 16-bit Windows. If you want to do things periodically, a worker thread is usually a better way to do it (and yes, Windows Mobile supports multiple threads).
Edit: in a dialog-based application, the main dialog exists for (essentially) the entire life of the application. Unless you really need the timer during the milliseconds between application startup and dialog creation or dialog destruction and application exit, just attach it to the dialog. Otherwise, you can attach it to the main window -- which MFC creates and destroys, even though it's never displayed.
If you use the MFC Wizard to create the Dialog based app, you probably have a hidden view window as well as a dialog window. The view window creates the dialog with DoModal(), which runs the dialog in the same thread, effectively suspending the view window.
While the dialog is open, the view window will not process any events. So, if the view window owns the timer, it will not process the timer events.
The simplest solution is to create the timer in the dialog and let the dialog handle the timer messages.
IMO, use the Timer if it solves the problem. As you've mentioned a Worker Thread interacting with the UI, in MFC, can be more trouble than its worth sometimes.
If the problem is simple enough for a timer to suffice, thats what i'd use (Remember KISS)
SetTimer does not have to be handed a window to work, it can call a callback method.
You can use that in your application - declare in your CWinApp (or anywhere really)
static void CALLBACK OnTimer(HWND, UINT, UINT, DWORD);
Then in the InitInstance call SetTimer(0, [eventid], [time period], OnTimer);
In OnTimer you can get back to the CWinApp instance via AfxGetApp() or theApp since there is only one.
Second attempt: my previous answer was dne in a hurry and was not correct.
Your basic vanilla MFC Dialog app only uses one thread. The main thread starts with a class derived from CWinApp. In the InitInstance() method it launches the dialog using CDialog::DoModal(). This function doesn't return until the dialog is closed.
While the dialog is running, the CWinApp class does not process any messages, so won't see a WM_TIMER.
There are many ways around this.
Let the first dialog own the timer and make all other dialogs children of it. This might be OK, depending on your dialog requirements, but it might be too restrictive.
Launch the first Dialog as modeless, i.e. use Create() instead of DoModal(). Create() returns straight away (putting the Dialog into a different thread). You can then create a message loop in the CWinApp class and process timers there. You'll have to use thread timers instead of window timers as the CWinApp class doesn't have a window. (or you could create a hidden window if that is more convenient).
You can hack the dialog's mesage loop and make it pass messages to the CWinApp class' message handler. That is quite complex and not for the faint hearted.
You can create a dedicated timer thread. You'd probably do that from the CWinApp class before it creates the dialog, but other strategies are possible.
Do any of those schemes sound like they fit your needs? If not, maybe you can explain your needs more fully and we might be able to come up with something appropriate.