Click to See Complete Forum and Search --> : Multithreading


NLscotty
August 27th, 2011, 06:00 PM
Hello,

I'm handling the messages of a window inside my class, but I would like that the program runs further without interference of the message handler. So I'm trying to do it like this

void CDialog::MessageHandler(HWND hwnd)
{
if(!AttachThreadInput(GetCurrentThreadId(),GetWindowThreadProcessId(hwnd, NULL),TRUE))
MessageBox(0,TEXT("Error"),TEXT("Error"),0);

MSG msg;

while(GetMessage( &msg, hwnd, 0, 0 ))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}

void CDialog::StartWindow()
{
CreateThread(0,0,(LPTHREAD_START_ROUTINE)MessageHandler,hWnd,0,0);
}


But since GetMessage() only seems to get messages inside the same thread it does not work, even with Attach to the thread not. Is it also needed to make a new thread for every window? Seems the best way for me.

Thanks,

NLscotty

Arjay
August 27th, 2011, 06:38 PM
Starting a thread for every window is most likely the wrong approach.

Consider following the best practice of putting all the GUI components (windows, controls, etc.) into the same (main) thread and put any long running operations into worker thread(s). Use notification messages to communicate between the worker threads and main thread.

NLscotty
August 28th, 2011, 02:07 AM
Starting a thread for every window is most likely the wrong approach.

Consider following the best practice of putting all the GUI components (windows, controls, etc.) into the same (main) thread and put any long running operations into worker thread(s). Use notification messages to communicate between the worker threads and main thread.

But when I do not create threads, thing like this are impossible

this->Initwindow(...);
this->StartWindow(...); // the message handler
this->....(); // this will never get hitted, so I assume I've to do this inside the WNDPROC?

Also big things which takes seconds to handle, will freeze the GUI thread, for this kind of stuff I've to use a worker thread?

A side note: I can define the worker thread and main thread? Since the debugger gives names to one allready.

EDIT: I did it in one thread, like I said above. Main GUI inside the thread and after that I do everything via WM_CREATE. But what if another thread would like to add something to a listbox, I can just pass the HWND from thread to thread. Is this not dangerous?

Arjay
August 28th, 2011, 11:29 AM
You are missing the point. You need to separate the business logic from the UI logic.

The UI code (logic) only has enough code to display the UI and allow the user to manipulate the UI.

The business code (logic) is where long running calculations are performed.

The UI code simply starts operations in the business logic layer where worker threads do the work. The worker threads notify the UI thread for changes in data or when an operation completes.

Lastly, worker threads should not manipulate any UI components from other threads - as it isn't thread safe.

NLscotty
August 30th, 2011, 12:04 PM
Thanks for the info, I'm doing what you are saying now. 1 thread for all UIs and the rest is side bussiness for threads.

But what if I would like to Add something to a listview? I should do a check inside the WNDPROC to a function if there is something in a global container and add that-> clean up afterwards?

Arjay
August 30th, 2011, 12:21 PM
But what if I would like to Add something to a listview? I should do a check inside the WNDPROC to a function if there is something in a global container and add that-> clean up afterwards?Please see my previous reply.

>> You need to separate the business logic from the UI logic.


Essentionally all work should be done within classes (logic, threading, data, etc). The UI connects to the logic for display and calls methods that the logic layer exposes.

With a proper design, you can change the way you display your data and operations with a minimum of code change.

The wrong way to do it is put the logic inside the UI, like calculation code inside a button handler for example.

This might seem like a harder way to do it, and for the purpose of school work it would be, but for a real complex application, having a clean separation of the UI and the logic, allows you to unit test the logic layer and implement the UI layer and debug it. When you have the two working separately, you put them together.

It's much tougher to try to get UI functionality working (like, disabling/hiding controls), when the logic is built into the UI.

MFC follows a design patterns called Doc/View, where the document is the logic (and data) and the view is the UI.

Newer design patterns for this sort of thing now are the Model View Controller, Model View Presenter, or Model View, View-Model patterns.

All of these design patterns achieve the goal of separation of UI and logic and provide other benefits.

NLscotty
August 30th, 2011, 12:38 PM
Please see my previous reply.

>> You need to separate the business logic from the UI logic.

.

But for example you do a huge calculation, there is a result, lets see for example -> x = 1. And I would like to add that to the listview, it has to somehow to send something to the UI thread right?

Arjay
August 30th, 2011, 01:14 PM
But for example you do a huge calculation, there is a result, lets see for example -> x = 1. And I would like to add that to the listview, it has to somehow to send something to the UI thread right?I answered that in post #2.

Use notification messages to communicate between the worker threads and main thread.

Have you looked at any threading examples? To start, check out the "Simple Thread: Part I" article listed in the 'my code guru articles" link in my signature line.

Igor Vartanov
August 31st, 2011, 08:34 AM
But what if I would like to Add something to a listview? I should do a check inside the WNDPROC to a function if there is something in a global container and add that-> clean up afterwards? No offense, but I believe much more fun would do experimenting instead of asking. When some idea comes to my mind I always make a simple prototype app to see if it works. While working on it I read any available documentation and internet posts related to the problem. Only when I find how it should be done, I move the found solution to my main working project. Because of this my habit you hardly can find a thread where I ask anything. And I wish you the same. ;)

And yes, the answer to your question is: you need to send LVM_INSERTITEM (http://www.google.ru/url?sa=t&source=web&cd=1&ved=0CBQQFjAA&url=http%3A%2F%2Fmsdn.microsoft.com%2Fen-us%2Flibrary%2Fbb761107(v%3Dvs.85).aspx&rct=j&q=LVM_INSERTITEM&ei=DzleTrDpBObbiAKtiPGyBQ&usg=AFQjCNHR94T1PMtt396izOT4qHicbFlLKw&cad=rja) message from your worker thread to the list view control to add an item. Windows API is thread safe. MFC is not.

MikeAThon
September 2nd, 2011, 01:47 PM
And yes, the answer to your question is: you need to send LVM_INSERTITEM (http://www.google.ru/url?sa=t&source=web&cd=1&ved=0CBQQFjAA&url=http%3A%2F%2Fmsdn.microsoft.com%2Fen-us%2Flibrary%2Fbb761107(v%3Dvs.85).aspx&rct=j&q=LVM_INSERTITEM&ei=DzleTrDpBObbiAKtiPGyBQ&usg=AFQjCNHR94T1PMtt396izOT4qHicbFlLKw&cad=rja) message from your worker thread to the list view control to add an item. Windows API is thread safe. MFC is not.
I would tend to disagree with this recommendation, and instead I would prefer Arjay's suggestion:
Use notification messages to communicate between the worker threads and main thread.
The problem with sending (SendMessage) an LVM_INSERTITEM from the worker thread to the UI thread is that the sending of the message will halt the worker thread until the UI thread returns a result. This has the big potential for thread lock if, for example, the UI thread is waiting on something else and is never able to send a response.

In other words, when using SendMessage, since the worker thread must wait for a response from the UI thread, there is far too much interaction between the worker thread and the UI thread.

Arjay's suggestion, on the other hand, is a clean break between the worker and UI threads. Use notification messages that you design for yourself.

One way to implement Arjay's suggestion is for the worker to allocate some memory on the heap (using "new", for example), and to store information in it that the worker wants the UI to have access to. The worker then posts (post using PostMessage, don't send using SendMessage) a custom notification message to the UI thread, using the LPARAM or WPARAM to store the address of the memory. The UI responds to the custom message by retrieving the memory, using it in some way (such as by inserting the answer x=1 into a list view, where the value of "1" was stored in the memory allocated by the worker), and then finally by free'ing the allocated memory (using "delete", for example).

The advantages to this framework include a very clean break between the UI and the worker. For example, since the worker is using PostMessage, it does not wait for the UI before proceeding with the next line of code. As a consequence, the worker will never freeze, even if the UI has become frozen (or indeed, even if the UI thread does not even exist). Likewise, the UI thread expects nothing from the worker, but if indeed it receives the custom notification message, it knows exactly how to respond.

Mike

PS: Note that you cannot PostMessage an LVM_INSERTITME from the worker to the UI, since the information contained in the LVM_INSERTITEM message will probably be out of scope by the time that the UI acts on the message.

Arjay
September 2nd, 2011, 06:31 PM
As mentioned, it's important to keep the UI operations and the worker thread operations separate.

As Mike suggested, one way to pass a string (or an object) is to new it up in the thread, then use PostMessage to pass the pointer where the UI thread receives the pointer and updates the UI elements.

This approach is pretty straightforward and works well, if the worker thread only passes a single type of data. It can be somewhat scalable to pass multiple types of data (just create different notification messages).

However, it becomes more unwieldy as different types of data are passed. Also, it isn't always convenient to have to 'new' up data to pass it. For example, who want's to have to 'new' up a long in order to pass it - as anything larger than an WParam or LParam will have to be 'new' up.

Another approach is to create a class that gets passed between the UI thread and the worker thread(s). This class contains private 'shared' data fields and exposes methods to access the data in a thread safe manner. The class also handles the creation, management and cleanup of the thread. The class exposes some Start and Stop methods which the UI connects to. It also contains static thread proc class method. Lastly, it exposes methods that set and get the data in a thread safe manner.

The whole approach uses PostMessage of a udm but only to notify the UI thread that the worker thread has made a change in the shared data.

In fact, the class methods used by the threads can completely encapsulate thread synchronization and Posting of notification methods. So the thread simply calls a method to set data. Inside that method, the notification message is posted, and when the message is received on the UI thread, another method is called to retrieve the data.

So let's say we have a worker thread that wants to pass some string data. When the string is changed, it gets added to a listbox.

Let's call the class we share between the UI thread and the worker thread "ThreadMgr".

This class exposes 3 methods that are accessed by the UI thread:
Start( HWND hWnd );
Stop( );
LPCTSTR GetData( );
The class also exposes two other methods accessed by the worker thread to set the string data and let the UI know when the worker thread has completed:
void SetData( LPCTSTR sData );
void Completed( );

The thread proc static method is defined as:
static UINT WINAPI ThreadProc( LPVOID lpContext )
{
// Turn the passed in 'this' pointer back into a Thread instance
ThreadMgr* pThreadMgr = reinterpret_cast< ThreadMgr* >( lpContext );

for( UINT uCount = 0; uCount < 100; uCount++ )
{
if( WAIT_OBJECT_0 == WaitForSingleObject( pThreadMgr->GetShutdownEvent( ), 0 ) )
{
return 1;
}

// Format a string and pass it to the UI thread
CString sData;

sData.Format( _T("String: %d"), uCount );
pThreadMgr->SetData( sData );
}

pThreadMgr->Completed( );

return 0;
}

You'll notice the Start method takes an HWND param. This is set by the UI and is the handle to the window that contains the UDM handler.

In addition, the Start method starts the worker thread. It starts the thread with (error checking removed). Notice the 'this' pointer gets passed when starting the thread?
m_hThread = (HANDLE)_beginthreadex(
NULL,
0,
ThreadProc,
static_cast<LPVOID>( this ),
0,
NULL);

The SetData (called by the worker thread) simply uses a critical section to lock access to the shared string, and then posts a message to the UI thread.
void SetData( LPCTSTR sData )
{
// auto lock access to the shared m_sData variable
AutoLock< CriticalSectionLock > lock( &m_csLock );

m_sData = sData;

// Post a message to the UI thread
::PostMessage( m_hWnd, WM_USER_UPDATE, 0, 0 );
}

In the UI thread, there is a message handler that calls the GetData method and inserts an item into a listbox.
LRESULT CMyDlg::OnUpdate( WPARAM wParam, LPARAM lParam )
{
m_ListBox.AddString( m_ThreadMgr.GetData( ) );

return 1;
}

Finally, the GetData method...
LPCTSTR GetData( )
{
// auto lock access to the shared m_sData variable
AutoLock< CriticalSectionLock > lock( &m_csLock );

return m_sData;
}

Sure, this is more code than 'new'ing up a pointer to a string, but the benefit is that the technique can be expanded to pass other types of data. Also, the interface used by the UI thread and worker thread is very clean. Both threads don't need to worry about thread synchronization issues, and the worker thread doesn't need to worry about posting messages to the UI thread.

Check out a sample that uses similar techniques to share a queue between threads while updating a couple of progress bars. http://www.codeguru.com/forum/showthread.php?p=1688454#post1688454

Igor Vartanov
September 2nd, 2011, 10:56 PM
Of course I agree with the said above with a single but seemingly important note: a somewhat complex cross thread data exchange mechanism is able to stump a beginner, and this way discourage him.