I'll work on absorbing the many points you made. Thanks for your help (and
touch the count-down which indicates completion by all threads. So it should
be safe.
it can handle messages from the ui-threads. There isn't a m_hWnd for it, so
it can't be the target of PostMessage or SendMessage. You mentioned sending
it WM_COMMAND's, but I am unfamiliar with how that routing would work. I
That doesn't seem like it would work ... as far as the "message pump"
delivering it to CMyFileContents. Is there another step involved in getting
variable that was an invisible CWnd that could be the target of PostMessage.
What I have in mind is simpler than a "thread pool". Also, I don't want to
report back each "hit". For now, I plan to have 4 ui threads each have
responsibility for 1/4th of a 4+mb file with about 40,000 lines. They each
"hits". The first thread might find "hits" in lines 1, 10, and 100. It would
2, with contents 1, 10, and 100.
The 2nd thread might find hits at lines 11000 and 12000. It will report 2
hits in indices 10000 and 10001.
20000, 20001, and 20002 are written.
many there were ... in this case 10. From the perspective of the owner data
filled.
Sound like a plan? You're pretty good at detecting flaws ... and I am the
grateful beneficiary.
"Joseph M. Newcomer" <newcomer@flounder.com> wrote in message
No, no concept of "static" is involved here. When a thread is running,
and it needs to
send something (such as PostMessage) to a CWnd, it is your responsibility
to make the
CWnd* pointer known to it. The simplest way is to pass it in as part of
the information
that comes in with the startup parameter (for a worker thread) or via the
WPARAM or LPARAM
information (for a UI thread). The concept of 'static' not only does not
exist, it would
probably be erroneous to involve any kind of static variable in the
effort.
On Wed, 9 Jan 2008 18:25:44 -0700, "L.Allan"
<lynn.d.allan@placeholder.gmail.com> wrote:
Thanks. I figured I could stumble around and come up with something that
works, but I wanted to check on what a "preferred solution" might be.
My "takeaway" is that when the multiple CMyUiThreads are constructed
and/or
initialized, they will be told of a (static?) CWnd* to report results to.
My (limited) understanding of the message pump mechanism is that I will
have
each of the ui threads use something like:
::SendMessage(m_hWndFrame,
UWM_THREAD_FIND_DONE,
foundCount, 0);
****
That's "PostMessage" not "SendMessage" that should be used! You would
also not do it this
way, which is calling a raw API function. What you would do is put the
CWnd* of your
receiving thread in that parameter packet you pass in via WPARAM or
LPARAM, and you would
write
wnd->PostMessage(UWM_THREAD_FIND_DONE, foundcount);
which is easier. Had you intended to write the low-level API, you would
write it as
::PostMessage, note the :: at the front! This scopes it to the global
scope and avoids
conflicts with the MFC method
****
Ok ... but I wonder why wnd->SendMessage wouldn't be suitable. That would
seem to "serialize" access and perhaps avoid a CRITICAL_SECTION. The
thread's are decrementing a counter of how many threads have "reported
back"
... which could result in a race condition? But perhaps I'm uninformed
(understatement <g>) ... a PostMessage would be just as serialized as a
SendMessage? The main UI thread would be checking this counter. (I'm
getting
a headache trying to mentally keep track of the messages flying around.)
****
It serializes access, and it also means that if there is any kind of
deadlock potential,
this will find it. I've had this happen. Suppose you SendMessage to the
main GUI thread,
and the main GUI thread does a SendMessage to your UI thread. Your main
thread and the UI
thread are mutually deadlocked and your app is effectively dead, dead,
dead. You keep the
counter in the main thread (the secondary thread never touches it) and
PostMessage works
just as well; until you finish the PostMessage processing, no other thread
(including the
main GUI thread itself) can possibly touch that counter. The difference
is that
SendMessage serializes the two threads completely, blocking the sender
until the recipient
returns. The main thread would not be "checking" the counter randomly; it
would only need
to look at it (a) when it creates a new thread [increments it] (b) when it
receives a
notification the thread has finished [decrements it] and (c) needs to
enable/disable
controls [reads it and compares it to 0]. In no case can there be a
conflict because the
main thread has sole responsibility for the counter, and it can only do
one thing at a
time.
There are really only two messages you are talking about here: the message
to provide the
UI thread with its parameters, and the message that is posted when the UI
thread
terminates its search, and consequently itself [actually, it doesn't have
to, you could
use a thread pool, but that adds a new layer of complexity, so let's
ignore that until you
are comfortable with the simple layer]. Right now, though, all the thread
does is say "I
found n items in file xxx.yyy", but it doesn't say where in the file it
found them, or
what else was there; I don't know if this is what you intended, but you
could add a third
message: "found string in file at position xxxxx" or something like that,
e.g.,
CString s;
LONGLONG linestart = 0;
while(file.ReadString(s))
{ /* read loop */
if(match_found_in(s))
{ /* found match */
MatchInfo * mi = new MatchInfo(linestart,
parms->fileid);
PostMessage(parms->target, UWM_MATCH_FOUND,
(WPARAM)mi);
} /* found match */
linestart = f.GetFilePos(); // I think this is the method...
} /* read loop */
which would record the fileid (remember I said you may want to pass this
in) and file
offset where the data was found, and your main thread would add this to
some table it was
maintaining.
So there really aren't a lot of messages flying around at all. Two or
three at most. So
this is actually a very simple threading example as these things go.
Life gets a lot more interesting if you have a *lot* of hits and you
saturate the
PostMessage queue, but you could either read my essay on this subject
(using I/O
Completion Ports) or even download my PowerPoint Indexer which does a lot
of interesting
stuff with interthread communication and uses this technique.
Key here is that exactly ONE thread has responsibility for the counter,
only ONE thread
reads it, and that is the SAME thread that increments it and decrements
it. That thread
can only do one thing at a time, and consequently there is no need for
synchronization
(read my essay "the best synchronization is no synchronization"). Yes,
there is some
really low-level synchronization required to implement storage allocation
and PostMessage,
but these are invisible to you nearly all the time, so you don't have to
reason about them
to make your code correct.
*****
to inform "somebody" when they are individually done. "Somebody" will
know
that 4 threads are involved, so a count-down will figure out when all
threads are done.
My understanding is that typically the CMainFrame class is the
normal/default recipient of user messages in a SDI app.
****
Actually, I'd send it to whatever thread launched the threads, most
likely
a view class.
****
My thinking was that this seemed to "couple" the view class too tightly
with
low-level actions that involve data. If anything, I wonder if coupling the
CMyUiThread's with the CMyDoc would be more appropriate. But it does seem
simpler to interact with CMyView. The "Find" button belonging to CMyView
is
clicked, the threads are triggered, and the results come back to the
CMyView
for organizing and presenting.
****
THere are arguments about using the view class for this purpose, which
mostly deal with
the fact that the DOCUMENT class is the actual data respository, but if
you PostMessage to
the document class (ignore for the moment this won't work) then it still
has to inform all
the views. Life gets interesting if you could close one of many views on
the document but
want the thread to keep running. In that case, you can see my essay on
how to write a
document timer event, and the trick is to create a dummy CWnd that is
invisible and is
owned by the CDocument-derived class, and you PostMessage to THAT. The
cool thing is that
given that you create the thread in the view and pass the parameters in
the view including
the target window (nominally the view) you could easily change this so tht
you create the
thread in the document, pass the parameters in the document including the
target window
(which is now the magic invisible window created by the document) AND YOUR
THREAD LOGIC
DOES NOT CHANGE A SINGLE LINE! It doesn't care if it is PostMessageing to
a view or some
random invisible window owned by the document, so you can decouple the
logic from the view
and put it inthe document (thus satisfying several esthetics about who is
responsble for
what) and the thread logic changes not one bit! Then you know you have a
properly-written
thread!
Generally, we interact with the one view because, as you point out, it is
easier, and we
simply act as if that view is the only view, or the dominant view, and if
the user closes
that view while the thread is running, we just terminate it cleanly and
that's that. But
in some contexts that won't really be right, hence the trick to move it
into the CDocument
in spite of the fact a CDocument can't be a target of a user-defined
message.
It isn't really holding your nose; it doesn't smell that bad. But it
looks a bit tacky.
*****
I've gotten the basic approach to work, but by violating data-hiding /
de-coupling principals by directly informing each of the threads of the
m_hWnd of the CMainFrame. The trying-to-be-more-of-a-purist in me
suspects
this is a "hold your nose" approach <g>.
****
You don't need to use an HWND, and you already have an OO way, which is
passing the CWnd*
of the target window. It may or may not be the main window, but the
thread doesn't have
to care.
****
Forehead, meet Palm <g>
****
Which explains why engineers have sloped foreheads...
****
I would greatly appreciate pointers on a preferred way to do this.
Should
CMyFileContents derive from CObject or something like CCmdTarget so it
can
be the recipient of messages such as UWM_THREAD_FIND_DONE? It seems like
the
UWM_THREADS_ALL_DONE message should get back to the CMyDoc, which is
what
holds the memory buffer of the file, and indirectly "kicks-off" the
"Find"
when the "Find" button handled by the CMyView is clicked.
****
No reason to derive from CObject; I usually use a very simple class, or
even 'this' to
pass a pointer to my view class. For your case, the creation of a
simple
class/struct to
hold the parameters is the best approach
****
CCmdTarget doesn't have a m_hWnd, but CView does.
****
CCmdTarget doesn't have an m_hWnd, but it can have WM_COMMAND messages
routed to it.
That's why it's called a "command" target. Note that the issue of
deriving or not
deriving from COjbect is irrelevant as to what CCmdTarget does or does not
have.
****
****
Take the simple approach. Tell the thread the CWnd* of its target and
let it go. It doesn't know what that CWnd* is, or care; its interface
spec is that it
will notify whatever CWnd* you tell it.
The key here is to understand what the "interface specification" to your
thread is. It
goes something like this
The thread receives a "work item" via PostThreadMessage, said work item
containing all the
parameters it needs to do its work
Part of its work is to notify someone when it has finished, so one of
the
values in the
work item is the window to notify
It will notify that window it has finished by sending a specific message
whose WPARAM is
the count of the number of items found (I might consider including which
thread this
represents, which would be to return some unique ID that is also passed
in
via the work
item, such as the index of this thread in the thread pool array
joe
****
Agreed. The CMyUiThread is told what "workerId" it is at contruction. This
workerId is WPARAM when it reports that it is done.
****
As long as there is some way to get that information across, fine. Note,
however, that if
you don't do it at construction but put it in the message you send, then
the thread could
actually be left sitting around in a "thread pool" if you needed it again.
Generally, try
to keep things out of the constructor so you don't get committed to having
to destroy the
thread when it finishes.
The model for thread pooling is:
Create a set of threads
When you need to have some work done, post a message to a non-busy thread
The thread's "I'm done" message just means it finished the current
transaction
To shut the thread down, PostMessage a "Please kill yourself" message
The thread's "I'm dead" message removes it from the pool
However, in this case, I prefer to create pools of pure worker threads
that block on an
I/O Completion Port. Then I PostQueuedCompletionStatus to that port.
Whichever thread is
blocked on it takes off and runs; all other threads that are blocked
remain blocked and
any thread that is running continues to run. The advantage here is you
don'tneed to keep
track of which thread is busy and which thread is idle; the I/O Completion
Port logic
handles all that nonsense for you. This is my preferred way to handle
pools of threads
where I need lots of bursts of concurrent computing but don't want to
track thread
availability myself, and thread creation overhead would kill me. To shut
them down, I
pust PostQueuedCompletionStatus a "please kill yourself" message, one per
thread, and
eventually they all shut down. But don't worry about thread pooling right
now; the cost
of I/O is so high you'll never notice the cost of the thread creation, so
keep it simple
until you are more comfortable with the paradigms.
joe
****
Thanks!
Joseph M. Newcomer [MVP]
email: newcomer@flounder.com
Web: http://www.flounder.com
MVP Tips: http://www.flounder.com/mvp_tips.htm