Multi Thread Debug
Multi Thread Debug
http://www.codeproject.com/useritems/ThreadVS2008.asp
VB.NET
All Topics, MFC/C++ >> C++ / MFC >> Unedited Reader Contributions
C++ (VC8.0) Windows (WinXP) Win32, VS (VS2008 Dev Posted Updated Views
ANNOUNCEMENTS
Visual Studio 2008 competition. $25,000 in prizes Jobs @ CodeProject Monthly Competition
Search
Print Broken Article? Bookmark
Articles Discuss
Go!
Send to a friend
Note: This is an unedited contribution. If this article is inappropriate, needs attention or copies someone else's work without reference then please Report this article.
Introduction
1 de 8
07/11/2007 9y37
http://www.codeproject.com/useritems/ThreadVS2008.asp
This article will use a sample MFC/C++ application to demonstrate the capabilities of the new Visual Studio 2008 debugger, specifically, debugging threads.
Background
A description of demonstrating the new thread debug window that comes as part of the Visual C++ 2008 Express Edition has been presented here. The reader should have a sufficient knowledge of threads on Windows platforms and a working knowledge of C++ and MFC. The reader should download a copy of the Visual C++ 2008 Express Edition. In addition, the user must have a C:\Program Files\Microsoft Visual Studio 8\VC\atlmfc folder present on their computer. I did this by using the atlmfc folder with Visual Studio 2005 on my machine, and copying the atlmfc folder to the appropriate place for the Visual C++ 2008 Express Edition, since the free download doesn't come with an atlmfc folder. This technique should work perfectly fine for the purposes of this article. Downloads for Visual Studio 2008 Express Edition (Beta) are obtained here: href="msdn2.microsoft.com/en-us/express/future/bb421473.asp"
2 de 8
07/11/2007 9y37
http://www.codeproject.com/useritems/ThreadVS2008.asp
void CSpinner::Draw() { // Get a pointer to the device context CDC *pDC = m_pViewWnd->GetDC(); // Set the mapping mode pDC->SetMapMode (MM_LOENGLISH); // Copy the spinner center CPoint org = m_pCenter; CPoint pStartPoint; // Set the starting point pStartPoint.x = (m_iRadius / 2); pStartPoint.y = (m_iRadius / 2); // Set the origination point org.x = m_pCenter.x + (m_iRadius / 2); org.y = m_pCenter.y + m_iRadius; // Set the viewport origination point pDC->SetViewportOrg(org.x, org.y); CPoint pEndPoint; // Calculate the angle of the next line double nRadians = (double) (m_nMinute * 6) * 0.017453292; // Set the end point of the line pEndPoint.x = (int) (m_iRadius * sin(nRadians)); pEndPoint.y = (int) (m_iRadius * cos(nRadians)); // Create the pen to use CPen pen(PS_SOLID, 0, m_crColors[m_crColor]); // Select the pen for use CPen* pOldPen = pDC->SelectObject(&pen); // Move to the starting point pDC->MoveTo (pEndPoint); // Draw the line to the end point pDC->LineTo (pStartPoint); // Reselect the previous pen pDC->SelectObject(&pOldPen); // Release the device context m_pViewWnd->ReleaseDC(pDC); // Increment the minute if (++m_nMinute == 60) { // If the minutes have gone full circle, reset to 0 m_nMinute = 0; // Increment the color if (++m_crColor == 8) // If we've gone through all colors, start again m_crColor = 0; } }
Pay close attention to the fact that the above code doesn't loop to draw lines on the screen. It does truly just "draw" one line at a single time. In order for the drawing to occur repeatedly, we have an endless loop installed in our thread function. We can "get away" with this seemingly bad coding by controlling Suspending and Resuming the threads during execution of the app.
3 de 8
07/11/2007 9y37
http://www.codeproject.com/useritems/ThreadVS2008.asp
UINT CTaskingDoc::ThreadFunc(LPVOID pParam) { // Convert the argument to a pointer to the // spinner for this thread CSpinner* lpSpin = (CSpinner*)pParam; while (TRUE) lpSpin->Draw(); // Spin the spinner return 0; }
Also, if you check out the declaration of the above function in CTaskingDoc, you'll see it listed as a "static" member. This is necessary since the function passed to AfxBeginThread must be of the form "UINT Controlling_function_name(void parameter)". It is important also to note that we truly only have two worker threads being spawned at any one time in our application. These are represented by the right hand side of the dialog box as "Thread 1" and "Thread 2." The other two checkboxes on the left side of our app, illustrate OnIdle processing! The reason why we include OnIdle calls becomes apparent when we talk about the Thread debugging window later in this article. For now, just note that the CSpinner::Draw() function is called a different way when we click "OnIdle call" check boxes, as shown below:
void CTaskingDoc::DoSpin(int nIndex) { // Spin the Spinner m_cSpin[nIndex].Draw(); }
OnIdle repeatedly calls CTaskingDoc::DoSpin, which fires whenever there is "idle time," hence the name. Whenever the application's message queue is empty, OnIdle will fire. This is why when clicking on the screen, the OnIdle calls are momentarily stopped, until the user is finished. We should note that the application's message queue is independent of our two worker threads running.
Debugging Threads
Now that you have some familiarity with the way the code works, let's turn our attention to how the Thread debugger can be used to look at some interesting things within this sample code. Let's first refer to one last piece of our code:
Collapse
4 de 8
07/11/2007 9y37
http://www.codeproject.com/useritems/ThreadVS2008.asp
void CTaskingDoc::SuspendSpinner(int nIndex, BOOL bSuspend) { // if suspending the thread if (!bSuspend) { // Is the pointer for the thread valid? if (m_pSpinThread[nIndex]) // Suspend the thread m_pSpinThread[nIndex]->SuspendThread(); } else // We are running the thread { // Is the pointer for the thread valid? if (m_pSpinThread[nIndex]) { // Resume the thread m_pSpinThread[nIndex]->ResumeThread(); } else { int iSpnr; int iPriority; // Which spinner to use? switch (nIndex) { case 0: iSpnr = 1; iPriority = THREAD_PRIORITY_NORMAL; break; case 1: iSpnr = 3; iPriority = THREAD_PRIORITY_LOWEST; break; } // Start the thread, passing a pointer to the spinner m_pSpinThread[nIndex] = AfxBeginThread(ThreadFunc, (LPVOID)&m_cSpin[iSpnr], iPriority); } } }
The main thing to watch here is at the end of this function, the call to AfxBeginThread. Worker threads are created using this function, and also notice that at the same time we can set the priority of the new worker thread as well. In our example, we compare priorities "Normal" and "Lowest." We place a breakpoint at the beginning of the ::SuspendSpinner call to illustrate our usage of the Thread debugger. Now start the application (in debug mode). Click on the application's check box labeled "Thread 1." You should hit our break point. Now, all that is left is select Debug->Windows->Threads from the IDE main menu. You'll see a window appear similar to the one shown below:
Notice a few things about this window. The ID and Name columns uniquely identify the thread. In this case, we are dealing with the main application thread, and no other threads have been created in our program! This makes
5 de 8
07/11/2007 9y37
http://www.codeproject.com/useritems/ThreadVS2008.asp
sense, since AfxBeginThread has not been called yet, which brings us to our next step. You may put a second break point on the AfxBeginThread and press F5 to move the execution of the program a little further. A small note here. I like to use the keyboard shortcut keys, and for Visual C++ 2008 Express they are the same as Visual Studio 6.0 (in the Visual 2005 environment they are slightly different). In any case, if you press F10 (step over) once, observe our new entry in the Thread Debug window!
Now, there are some things to take note of here. The color of the Main Thread is always green, Worker Threads are always in yellow. Also note that the priorities of both of these threads are "Normal," plus neither of these threads have been Suspended (Suspend == 0). There is also a neat little feature that you can try, place your cursor on the Name column, and right mouse click. There is an option to rename the thread to something meaningful (perhaps Thread1). This becomes extremely useful if we are debugging an application that makes use of many threads. Press F5 to run our program a little further, but this time select "Thread 2" and pay close attention to what happens when we step just past the AfxBeginThread call. You probably guessed that our second worker thread would show up, and this time, the Priority is set to "Lowest." This matches how the thread was created if you observe the parameter fed to AfxBeginThread. You should see a window similar to the one below:
We included OnIdle processing in this article just to illustrate that really no new threads get generated in the application as a result of their calls. You may put a break point in the OnIdle calls, observe the Thread Debug window, and notice that no additional threads get created. Now that we have created these two worker threads, and they are running in our application, you can click on either one of these (or both) of the check boxes and observe the Suspend column in our Thread debugging window get set to "1."
There is also one more "trick" concerning the Thread debugging window. At any time when using the debugging window, you may place your cursor over
6 de 8
07/11/2007 9y37
http://www.codeproject.com/useritems/ThreadVS2008.asp
the execution pointer, and observe the call stack! Remember that all threads have their own execution stack, including the Main execution thread. Pretty neat, eh? Well, you have everything you need to begin using the Thread debugger in Visual C++ 2008 when it finally gets released, I'm told in February!
Reference
This code has been modified from the orignial in SAMS Teach Yourself Visual C++ 6 in 21 Days -- David Chapman with Jeff Heaton.
History
Version 1.0.1 (Major, Minor, Patch)
About JediScientist
Electrical Engineer that loves mathematics and C# and C++ programming and electronics. I am a Senior Software Engineer at a company that is a leader in the biometrics field. I get to work on really cool things, like scanning an Iris or a fingerprint, and an opportunity to put image processing mathematics to good use, using MatLab. I speak a little Espaol, Portuguese, and my next attempt is going to be German: Les acclamations, de la Floride et mnent la vie au fullest! Most of all, I have a great son and daughter that I am very proud of, and I dedicate all my codeproject articles to them. favorite movie quote: "Remember: Your focus determines your reality." Click here to view JediScientist's online profile.
Symbolic Differentiation
This article demonstrates differentiating expressions using a stack and displaying the input expression and its derivative.
[Top]
Poor n n n n n Excellent j j j j j k k k k k l l l l l m m m m m
Vote
7 de 8
07/11/2007 9y37
http://www.codeproject.com/useritems/ThreadVS2008.asp
FAQ
6 6
Set Options
6
First Prev Next Date 12:50 31 Oct '07 13:32 31 Oct '07 13:53 31 Oct '07 21:34 30 Oct '07 22:26 30 Oct '07 First Prev Next
Msgs 1 to 5 of 5 (Total: 5) (Refresh) Author Daniel Moth JediScientist Daniel Moth x-cubed JediScientist
Re: Plug: video of this Re: Plug: video of this Your image URLs are incorrect Re: Your image URLs are incorrect Last Visit: 9:28 Tuesday 30th October, 2007 General comment message News / Info Question
Answer
Joke / Game
Admin
Article content copyright JediScientist, 2007 everything else Copyright CodeProject, 1999-2007. Web13 | Advertise on The Code Project | Privacy
The Ultimate Toolbox ASP Alliance Developer Fusion Developersdex DevGuru Programmers Heaven Planet Source Code Tek-Tips Forums
8 de 8
07/11/2007 9y37