?? ch27.htm
字號:
Stop Thread command to the Thread menu.</I></P>
<DL>
<DT><I></I></DT>
<P>
<DD><B>2.</B> Use ClassWizard to associate the ID_STOPTHREAD command with the OnStopthread()
message-response function, as shown in Figure 27.7. Make sure that you have <B>CThreadView</B>
selected in the Class Name box before you add the function. Add the following line
to the OnStopthread() function, replacing the TODO: Add your command handler code
here comment:
</DL>
<BLOCKQUOTE>
<PRE>threadController = 0;</PRE>
</BLOCKQUOTE>
<PRE></PRE>
<P><A HREF="javascript:popUp('27uvc07.gif')"><B>FIG. 27.7</B></A><B> </B><I>Add the
OnStopthread() message-response function.</I></P>
<P><I></I></P>
<DL>
<DD>This refers to a new global variable you are about to declare.
<P>
<DT></DT>
<DD><B>3. </B>Add the following line to the top of the ThreadView.cpp file, right
after the endif directive:
</DL>
<BLOCKQUOTE>
<PRE>volatile int threadController;</PRE>
</BLOCKQUOTE>
<PRE></PRE>
<DL>
<DT></DT>
<DD>The volatile keyword means that you expect this variable will be changed from
outside a thread that uses it. The keyword requests that the compiler not cache the
variable in a register or in any way count on the value staying unchanged just because
code in one thread doesn't seem to change it.
<P>
<DT></DT>
<DD><B>4. </B>Add the following line to the OnStartthread() function, before the
two lines you added earlier:
</DL>
<BLOCKQUOTE>
<PRE> threadController = 1;</PRE>
</BLOCKQUOTE>
<PRE></PRE>
<DL>
<DT></DT>
<DD>By now, perhaps, you've guessed that the value of threadController determines
whether the thread will continue. Replace the ThreadProc() function with the one
shown in Listing 27.2.
<P>
</DL>
<H4>Listing 27.2  The New ThreadProc() Function</H4>
<PRE>UINT ThreadProc(LPVOID param)
{
::MessageBox((HWND)param, "Thread activated.", "Thread", MB_OK);
while (threadController == 1)
{
;
}
::MessageBox((HWND)param, "Thread stopped.", "Thread", MB_OK);
return 0;
</PRE>
<PRE>}
</PRE>
<P>Now the thread first displays a message box, telling the user that the thread
is starting. Then a while loop continues to check the threadController global variable,
waiting for its value to change to 0. Although this while loop is trivial, it is
here that you would place the code that performs whatever task you want the thread
to perform, making sure not to tie things up for too long before rechecking the value
of threadController.</P>
<P>Try a test: Build and run the program, and choose Thread, Start Thread to start
the secondary thread. When you do, a message box appears, telling you that the new
thread was started. To stop the thread, select the Thread, Stop Thread command. Again,
a message box appears, this time telling you that the thread is stopping.</P>
<BLOCKQUOTE>
<P>
<HR>
<strong>CAUTION:</strong><B> </B>Using global variables to communicate between threads is,
to say the least, an unsophisticated approach to thread communication and can be
a dangerous technique if you're not sure how C++ handles variables from an assembly-language
level. Other thread-communication techniques are safer and more elegant.
<HR>
</BLOCKQUOTE>
<H3><A NAME="Heading4"></A>Communicating with User-Defined Messages</H3>
<P>Now you have a simple, albeit unsophisticated, method for communicating information
from your main program to your thread. How about the reverse? That is, how can your
thread communicate with the main program? The easiest method to accomplish this communication
is to incorporate user-defined Windows messages into the program.</P>
<P>The first step is to define a user message, which you can do easily, like this:</P>
<P>
<PRE>const WM_USERMSG = WM_USER + 100;
</PRE>
<P>The WM_USER constant, defined by Windows, holds the first available user-message
number. Because other parts of your program may use some user messages for their
own purposes, the preceding line sets WM_USERMSG to WM_USER+100.</P>
<P>After defining the message, you call ::PostMessage() from the thread to send the
message to the main program whenever you need to. (Message handling was discussed
in Chapter 3, "Messages and Commands." Sending your own messages allows
you to take advantage of the message-handling facility built into MFC.) A typical
call to ::PostMessage() might look like this:</P>
<P>
<PRE>::PostMessage((HWND)param, WM_USERMSG, 0, 0);
</PRE>
<P>PostMessage()'s four arguments are the handle of the window to which the message
should be sent, the message identifier, and the message's WPARAM and LPARAM parameters.</P>
<P>Modify the Thread application according to the next steps to see how to implement
posting user messages from a thread.</P>
<DL>
<DT></DT>
<DD><B>1. </B>Add the following line to the top of the ThreadView.h header file,
right before the beginning of the class declaration:
<P>
</DL>
<PRE>const WM_THREADENDED = WM_USER + 100;
</PRE>
<DL>
<DT></DT>
<DD><B>2.</B> Still in the header file, add the following line to the message map,
right after the //{{AFX_MSG(CThreadView) comment and before DECLARE_MESSAGE_MAP:
<P>
</DL>
<PRE> afx_msg LONG OnThreadended(WPARAM wParam, LPARAM lParam);
</PRE>
<DL>
<DT></DT>
<DD><B>3. </B>Switch to the ThreadView.cpp file and add the following line to the
class's message map, making sure to place it right <I>after</I> the }}AFX_MSG_MAP
comment:
<P>
</DL>
<PRE> ON_MESSAGE(WM_THREADENDED, OnThreadended)
</PRE>
<DL>
<DT></DT>
<DD><B>4. </B>Replace the ThreadProc() function with the one shown in Listing 27.3.
<P>
</DL>
<H4>Listing 27.3  The Message-Posting ThreadProc()</H4>
<PRE>UINT ThreadProc(LPVOID param)
{
::MessageBox((HWND)param, "Thread activated.", "Thread", MB_OK);
while (threadController == 1)
{
;
}
::PostMessage((HWND)param, WM_THREADENDED, 0, 0);
return 0;
</PRE>
<PRE>}
</PRE>
<DL>
<DT></DT>
<DD><B>5. </B>Add the function shown in Listing 27.4 to the end of the ThreadView.cpp
file.
<P>
</DL>
<H4>Listing 27.4  CThreadView::OnThreadended()</H4>
<PRE>LONG CThreadView::OnThreadended(WPARAM wParam, LPARAM lParam)
{
AfxMessageBox("Thread ended.");
return 0;
</PRE>
<PRE>}
</PRE>
<P>When you run the new version of the Thread program, select the Thread, Start Thread
command to start the thread. When you do, a message box appears, telling you that
the thread has started. To end the thread, select the Thread, Stop Thread command.
Just as with the previous version of the program, a message box appears, telling
you that the thread has ended.</P>
<P>Although this version of the Thread application seems to run identically to the
previous version, there's a subtle difference. Now the program displays the message
box that signals the end of the thread in the main program rather than from inside
the thread. The program can do this because, when the user selects the Stop Thread
command, the thread sends a WM_THREADENDED message to the main program. When the
program receives that message, it displays the final message box.</P>
<P>
<H3><A NAME="Heading5"></A>Communicating with Event Objects</H3>
<P>A slightly more sophisticated method of signaling between threads is to use <I>event
objects,</I> which under MFC are represented by the CEvent class. An event object
can be in one of two states: signaled and nonsignaled. Threads can watch for events
to be signaled and so perform their operations at the appropriate time. Creating
an event object is as easy as declaring a global variable, like this:</P>
<P>
<PRE>CEvent threadStart;
</PRE>
<P>Although the CEvent constructor has a number of optional arguments, you can usually
get away with creating the default object, as shown in the previous line of code.
On creation, the event object is automatically in its nonsignaled state. To signal
the event, you call the event object's SetEvent() member function, like this:</P>
<P>
<PRE>threadStart.SetEvent();
</PRE>
<P>After the preceding line executes, the threadStart event object will be in its
signaled state. Your thread should be watching for this signal so that the thread
knows it's okay to get to work. How does a thread watch for a signal? By calling
the Windows API function, WaitForSingleObject():</P>
<P>
<PRE>::WaitForSingleObject(threadStart.m_hObject, INFINITE);
</PRE>
<P>This function's two arguments are</P>
<UL>
<LI>The handle of the event for which to check (stored in the event object's m_hObject
data member)
<P>
<LI>The length of time the function should wait for the event
</UL>
<P>The predefined INFINITE constant tells WaitForSingleObject() not to return until
the specified event is signaled. In other words, if you place the preceding line
at the beginning of your thread, the system suspends the thread until the event is
signaled. Even though you've started the thread execution, it's halted until whatever
you need to happen happens. When your program is ready for the thread to perform
its duty, you call the SetEvent() function, as previously described.</P>
<P>When the thread is no longer suspended, it can go about its business. However,
if you want to signal the end of the thread from the main program, the thread must
watch for this next event to be signaled. The thread can do this by polling for the
event. To poll for the event, you again call WaitForSingleObject(), only this time
you give the function a wait time of 0, like this:</P>
<P>
<PRE>::WaitForSingleObject(threadend.m_hObject, 0);
</PRE>
<P>In this case, if WaitForSingleObject() returns WAIT_OBJECT_0, the event has been
signaled. Otherwise, the event is still in its nonsignaled state.</P>
<P>To better see how event objects work, follow these steps to further modify the
Thread application:</P>
<DL>
<DT></DT>
<DD><B>1. </B>Add the following line to the top of the ThreadView.cpp file, right
after the line #include "ThreadView.h":
<P>
</DL>
<PRE>#include "afxmt.h"
</PRE>
<DL>
<DT></DT>
<DD><B>2. </B>Add the following lines near the top of the ThreadView.cpp file, after
the volatile int threadController line that you placed there previously:
<P>
</DL>
<PRE>CEvent threadStart;
CEvent threadEnd;
</PRE>
<DL>
<DT></DT>
<DD><B>3. </B>Delete the volatile int threadController line from the file.
<P>
<DT></DT>
<DD><B>4. </B>Replace the ThreadProc() function with the one shown in Listing 27.5.
<P>
</DL>
<H4>Listing 27.5  Yet Another ThreadProc()</H4>
<PRE>UINT ThreadProc(LPVOID param)
{
::WaitForSingleObject(threadStart.m_hObject, INFINITE);
::MessageBox((HWND)param, "Thread activated.",
"Thread", MB_OK);
BOOL keepRunning = TRUE;
while (keepRunning)
{
int result =
::WaitForSingleObject(threadEnd.m_hObject, 0);
if (result == WAIT_OBJECT_0)
keepRunning = FALSE;
}
::PostMessage((HWND)param, WM_THREADENDED, 0, 0);
return 0;
</PRE>
<PRE>}
</PRE>
<DL>
<DT></DT>
<DD><B>5. </B>Replace all the code in the OnStartthread() function with the following
line:
<P>
</DL>
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -