📄 ch27.htm
字号:
<pre><font color="#008000"> HWND hWnd = GetSafeHwnd();</font></pre>
<pre><font color="#008000"> AfxBeginThread(ThreadProc, hWnd, THREAD_PRIORITY_NORMAL);</font></pre>
<P>This code will call a function called ThreadProc within a thread of its own. Next, add ThreadProc, shown in Listing 27.1, to ThreadView.cpp, placing it right before the <font color="#008000">OnStartthread()</font> function. Note that <font
color="#008000">ThreadProc()</font> is a global function and not a member function of the <font color="#008000">CThreadView</font> class, even though it is in the view class's implementation file.</P>
<P><I>Listing 27.1—ThreadView.cpp—ThreadProc()</I></P>
<pre><font color="#008000">UINT ThreadProc(LPVOID param)</font></pre>
<pre><font color="#008000">{</font></pre>
<pre><font color="#008000"> ::MessageBox((HWND)param, "Thread activated.", "Thread", MB_OK);</font></pre>
<pre><font color="#008000"> return 0;</font></pre>
<pre><font color="#008000">}</font></pre>
</ol>
<P>This threaded function doesn't do much, just reports that it was started. The SDK function <font color="#008000">MessageBox()</font> is very much like <font color="#008000">AfxMessageBox()</font>, but since this is not a member function of a class
derived from CWnd, you cannot use <font color="#008000">AfxMessageBox()</font>.</P>
<blockquote><p><img src="tip.gif" tppabs="http://www.mcp.com/814147200/0-7897/0-7897-1145-1/tip.gif">
<P>The double colons in front of a function name indicate a call to a global function, rather than an MFC class member function. For Windows programmers this usually means an API or SDK call. For example, inside an MFC window class, you can call <font
color="#008000">MessageBox("Hi, There!")</font> to display "Hi, There!" to the user. This form of <font color="#008000">MessageBox()</font> is a member function of the MFC window classes. To call the original Windows version , you'd
write something like <font color="#008000">::MessageBox(0, "Hi, </font><font color="#008000">There!", "Message", MB_OK)</font>. Notice the colons in front of the function name and the additional arguments.</P>
<p><img src="bottom.gif" tppabs="http://www.mcp.com/814147200/0-7897/0-7897-1145-1/bottom.gif"></blockquote>
<P>When you run the Thread program, the main window appears. Select the <U>T</U>hread, <U>S</U>tart Thread command, and the system starts the thread represented by the <font color="#008000">ThreadProc()</font> function and displays a message box, as shown
in Figure 27.5.</P>
<A HREF="BBfig05.gif" tppabs="http://www.mcp.com/814147200/0-7897/0-7897-1145-1/figs/ch27/BBfig05.gif"><b>Fig. 27.5</b></A>
<P><I>The simple secondary thread in the Thread program displays a message box </I><I>and then ends.</I></P>
<H3><B>Understanding Thread Communication</B></H3>
<P>Usually, a secondary thread performs some sort of task for the main program, which implies that there needs to be a channel of communication between the program (which is also a thread) and its secondary threads. There are several ways to accomplish
these communications tasks: using global variables, using event objects, and using messages. In this section, you'll explore these thread-communication techniques.</P>
<P><B>Communicating with Global Variables</B></P>
<P>Suppose you want your main program to be able to stop the thread. You need a way, then, to tell the thread when to stop. One way to do this is to set up a global variable and then have the thread monitor the global variable for a value that signals the
thread to end. To see how this technique works, modify the Thread application as follows:</P>
<P>First, use the resource editor to add a S<U>t</U>op Thread command to the application's <U>T</U>hread menu. Give this new command the <font color="#008000">ID_STOPTHREAD</font> ID, as shown in Figure 27.6.</P>
<A HREF="BBfig06.gif" tppabs="http://www.mcp.com/814147200/0-7897/0-7897-1145-1/figs/ch27/BBfig06.gif"><b>Fig. 27.6</b></A>
<P><I>Add a S</I><I><U>t</U></I><I>op Thread command to the </I><I><U>T</U></I><I>hread menu.</I></P>
<P>Use ClassWizard to associate the <font color="#008000">ID_STOPTHREAD</font> command with the <font color="#008000">OnStopthread()</font> message-response function, as shown in Figure 27.7. Make sure that you have <font
color="#008000">CThreadView</font> selected in the Class <U>N</U>ame box before you add the function. Add the following line to the <font color="#008000">OnStopthread()</font> function, replacing the <font color="#008000">TODO: Add your command handler
code here</font> comment:</P>
<pre><font color="#008000">threadController = 0;</font></pre>
<A HREF="BBfig07.gif" tppabs="http://www.mcp.com/814147200/0-7897/0-7897-1145-1/figs/ch27/BBfig07.gif"><b>Fig. 27.7</b></A>
<P><I>Add the </I><I>OnStopthread()</I><I> message-response function.</I></P>
<P>This refers to a new global variable you are about to declare. Add the following line to the top of the ThreadView.cpp file, right after the <font color="#008000">endif</font> directive:</P>
<pre><font color="#008000">volatile int threadController;</font></pre>
<P>The <font color="#008000">volatile</font> 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>
<P>Add the following line to the <font color="#008000">OnStartthread()</font> function, before the two lines you added earlier:</P>
<pre><font color="#008000"> threadController = 1;</font></pre>
<P>By now perhaps you've guessed that the value of threadController determines whether the thread will continue or not. Replace the <font color="#008000">ThreadProc()</font> function with the one shown in Listing 27.2.</P>
<P><I>Listing 27.2—The New </I>ThreadProc()<I> Function</I></P>
<pre><font color="#008000">UINT ThreadProc(LPVOID param)</font></pre>
<pre><font color="#008000">{</font></pre>
<pre><font color="#008000"> ::MessageBox((HWND)param, "Thread activated.", "Thread", MB_OK);</font></pre>
<pre><font color="#008000"> </font></pre>
<pre><font color="#008000"> while (threadController == 1)</font></pre>
<pre><font color="#008000"> {</font></pre>
<pre><font color="#008000"> ;</font></pre>
<pre><font color="#008000"> }</font></pre>
<pre><font color="#008000"> ::MessageBox((HWND)param, "Thread stopped.", "Thread", MB_OK);</font></pre>
<pre><font color="#008000"> return 0;</font></pre>
<pre><font color="#008000">}</font></pre>
<P>Now the thread first displays a message box, telling the user that the thread is starting. Then a <font color="#008000">while</font> loop continues to check the <font color="#008000">threadController</font> global variable, waiting for its value to
change to 0. Although this <font color="#008000">while</font> loop is trivial, it is here that you would place the code that performs whatever task you want the thread to perform, being sure not to tie things up for too long before rechecking the value of
<font color="#008000">threadController</font>.</P>
<P>Try a test: build and run the program, and choose <U>T</U>hread, <U>S</U>tart 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 <U>T</U>hread, Stop
<U>T</U>hread command. Again, a message box appears, this time telling you that the thread is stopping.</P>
<blockquote><p><img src="caution.gif" tppabs="http://www.mcp.com/814147200/0-7897/0-7897-1145-1/caution.gif">
<P>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.</P>
<p><img src="bottom.gif" tppabs="http://www.mcp.com/814147200/0-7897/0-7897-1145-1/bottom.gif"></blockquote>
<P><B>Communicating with User-Defined Messages</B></P>
<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>
<pre><font color="#008000">const WM_USERMSG = WM_USER + 100;</font></pre>
<P>The <font color="#008000">WM_USER</font> constant, defined by Windows, holds the first available user-message number. Because other parts of your program may use some of the user messages for their own purposes, the preceding line sets <font
color="#008000">WM_USERMSG</font> to <font color="#008000">WM_USER+100</font>.</P>
<P>After defining the message, you call <font color="#008000">::PostMessage()</font> from the thread in order to send the message to the main program whenever you need to. (Message handling was discussed in <A HREF="index04.htm" tppabs="http://www.mcp.com/814147200/0-7897/0-7897-1145-1/index04.htm" target="text">Chapter
4</A>, "Messages and Commands." Sending your own messages allows you take advantage of the message-handling facility built into MFC.) A typical call to <font color="#008000">::PostMessage()</font> might look like this:</P>
<pre><font color="#008000">::PostMessage((HWND)param, WM_USERMSG, 0, 0);</font></pre>
<p><font color="#008000">PostMessage()</font>'s four arguments are the handle of the window to which the message should be sent, the message identifier, and the message's <font color="#008000">WPARAM</font> and <font color="#008000">LPARAM</font>
parameters.</P>
<P>Modify the Thread application according to the next steps in order to see how to implement posting user messages from a thread.</P>
<ol>
<li><P> Add the following line to the top of the ThreadView.h header file, right before the beginning of the class declaration:</P>
<pre><font color="#008000">const WM_THREADENDED = WM_USER + 100;</font></pre>
<li><P> Still in the header file, add the following line to the message map, right after the <font color="#008000">AFX_MSG</font> comment and before <font color="#008000">DECLARE_MESSAGE_MAP</font>:</P>
<pre><font color="#008000"> afx_msg LONG OnThreadended(WPARAM wParam, LPARAM lParam);</font></pre>
<li><P> 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 <font color="#008000">}}AFX_MSG_MAP</font> comment:</P>
<pre><font color="#008000"> ON_MESSAGE(WM_THREADENDED, OnThreadended)</font></pre>
<li><P> Replace the <font color="#008000">ThreadProc()</font> function with the one shown in Listing 27.3.</P>
<P><I>Listing 27.3—The Message-Posting </I>ThreadProc()<I> </I></P>
<pre><font color="#008000">UINT ThreadProc(LPVOID param)</font></pre>
<pre><font color="#008000">{</font></pre>
<pre><font color="#008000"> ::MessageBox((HWND)param, "Thread activated.", "Thread", MB_OK);</font></pre>
<pre><font color="#008000"> </font></pre>
<pre><font color="#008000"> while (threadController == 1)</font></pre>
<pre><font color="#008000"> {</font></pre>
<pre><font color="#008000"> ;</font></pre>
<pre><font color="#008000"> }</font></pre>
<pre><font color="#008000"> ::PostMessage((HWND)param, WM_THREADENDED, 0, 0);</font></pre>
<pre><font color="#008000"> return 0;</font></pre>
<pre><font color="#008000">}</font></pre>
<li><P> Add the function shown in Listing 27.4 to the end of the ThreadView.cpp file.</P>
<P><I>Listing 27.4— CThreadView::OnThreadended()</I></P>
<pre><font color="#008000">LONG CThreadView::OnThreadended(WPARAM wParam, LPARAM lParam)</font></pre>
<pre><font color="#008000">{</font></pre>
<pre><font color="#008000"> AfxMessageBox("Thread ended.");</font></pre>
<pre><font color="#008000"> return 0;</font></pre>
<pre><font color="#008000">}</font></pre>
</ol>
<P>When you run the new version of the Thread program, select the <U>T</U>hread, <U>S</U>tart 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 <U>T</U>hread, Stop
<U>T</U>hread 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 that from inside the
thread. The program can do this because, when the user selects the Stop <U>T</U>hread command, the thread sends a <font color="#008000">WM_THREADENDED</font> message to the main program. When the program receives that message, it displays the final message
box.</P>
<P><B>Communicating with Event Objects</B></P>
<P>A slightly more sophisticated method of signaling between threads is to use <I>event objects,</I> which under MFC are represented by the <font color="#008000">CEvent</font> 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>
<pre><font color="#008000">CEvent threadStart;</font></pre>
<P>Although the <font color="#008000">CEvent</font> 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. Upon creation, the event object is automatically in its
nonsignaled state. To signal the event, you call the event object's <font color="#008000">SetEvent()</font> member function, like this:</P>
<pre><font color="#008000">threadStart.SetEvent();</font></pre>
<P>After the preceding line executes, the <font color="#008000">threadStart</font> 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, <font color="#008000">WaitForSingleObject()</font>:</P>
<pre><font color="#008000">::WaitForSingleObject(threadStart.m_hObject, INFINITE);</font></pre>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -