好就没有写自己的技术博客了,信手翻来,发现已是半年前的情形了。漫步网页间,居然搜索到了自己一直关心的咚咚。毕竟还是对C++的那种眷恋,QT也好,TBB也好,Meego也好,都还有有着那份关注。
今天看到的这篇文章是从MSDN里摘录下来的,毕竟ppl还是比较新的。相信国内还有很多人未曾使用过这类东东。以前可以用openmp来做多核计算(其实Intel CPU下TBB更好用些),到了vs 2010里完全可以用微软提供的这个ppl.h来做。长话短说,贴上,看到的咚咚。
转自:http://msdn.microsoft.com/en-us/library/dd554943.aspx
This walkthrough shows how to use the Parallel Tasks andParallel Stacks windows to debug a parallel application. These windows help you understand and verify the runtime behavior of code that uses theTask Parallel Library or theConcurrency Runtime. This walkthrough provides sample code that has built-in breakpoints. After the code breaks, the walkthrough shows how to use theParallel Tasks and Parallel Stacks windows to examine it.
This walkthrough teaches these tasks:
How to view the call stacks of all threads in one view.
How to view the list of System.Threading.Tasks.Task instances that are created in your application.
How to view the real call stacks of tasks instead of threads.
How to navigate to code from the Parallel Tasks andParallel Stacks windows.
How the windows cope with scale through grouping, zooming, and other related features.
You must have Visual Studio 2010 installed on the computer.
This walkthrough assumes that Just My Code is enabled. On theTools menu, clickOptions, expand theDebugging node, selectGeneral, and then selectEnable Just My Code (Managed only). If you do not set this feature, you can still use this walkthrough, but your results may differ from the illustrations.
C# Sample
If you use the C# sample, this walkthrough also assumes that External Code is hidden. To toggle whether external code is displayed, right-click theName table header of theCall Stack window, and then select or clearShow External Code. If you do not set this feature, you can still use this walkthrough, but your results may differ from the illustrations.
C++ Sample
If you use the C++ sample, you can ignore references to External Code in this topic. External Code only applies to the C# sample.
Illustrations
The illustrations in this topic recorded on a quad core computer running the C# sample. Although you can use other configurations to complete this walkthrough, the illustrations may differ from what is displayed on your computer.
Creating the Sample ProjectThe sample code in this walkthrough is for an application that does nothing. The goal is just to understand how to use the tool windows to debug a parallel application.
To create the sample project
In Visual Studio, on the File menu, point to New and then click Project.
In the Installed Templates pane, select either Visual C#, Visual Basic, or Visual C++. For the managed languages, ensure that .NET Framework 4 is displayed in the framework box.
Select Console Application and then click OK. Remain in Debug configuration, which is the default.
Open the .cpp, .cs, or .vb code file in the project. Delete its contents to create an empty code file.
Paste the following code for your chosen language into the empty code file.
#include "stdafx.h"
#include <windows.h>
#include <iostream>
#include <ppl.h>
#include <agents.h>
#include <stdio.h>
#include <concrtrm.h>
#include <vector>
CRITICAL_SECTION cs;
using namespace ::std;
using namespace ::std::tr1;
using namespace ::Concurrency;
task_group task4;
task_group task3;
task_group task2;
volatile long aa = 0;
volatile long bb = 0;
volatile long cc = 0;
static bool waitFor1 = true;
static bool waitFor5 = true;
#pragma optimize("", off)
void Spin()
{
for(int i=0;i<50*50000;++i);
}
#pragma optimize("",on)
template<class Func>
class RunFunc
{
Func& m_Func;
int m_o;
public:
RunFunc(Func func,int o):m_Func(func),m_o(o){
};
void operator()()const{
m_Func(m_o);
};
};
void T(int o)
{
cout << "Scheduled run \n";
};
void R(int o)
{
if (o == 2)
{
while (waitFor5) { ;}
Spin();
//use up all processors but 4 by scheduling 4 non-terminating tasks.
int numProcsToBurn = GetProcessorCount() - 4;
int i;
vector<call<int>*> tasks;
for (i = 0; i < numProcsToBurn; i++)
{
tasks.push_back(new call<int>([](int i){while(true)Spin();}));
asend(tasks[i],1);
cout << "Started task \n";
}
task_handle<RunFunc<decltype(T)>> t6(RunFunc<decltype(T)>(T,i + 1 + 5));
task_handle<RunFunc<decltype(T)>> t7(RunFunc<decltype(T)>(T,i + 2 + 5));
task_handle<RunFunc<decltype(T)>> t8(RunFunc<decltype(T)>(T,i + 3 + 5));
task_handle<RunFunc<decltype(T)>> t9(RunFunc<decltype(T)>(T,i + 4 + 5));
task_handle<RunFunc<decltype(T)>> t10(RunFunc<decltype(T)>(T,i + 5 + 5));
task2.run(t6);
task2.run(t7);
task2.run(t8);
task2.run(t9);
task2.run(t10);
//BP4 - 1 in M, 2 in R, 3 in J, 4 in R, 5 died
DebugBreak();
}
else
{
if (o!=4)
throw;
task3.wait();
}
};
void Q()
{
// breaks here at the same time as N and M
InterlockedIncrement(& cc);
while (cc < 4)
{
;
}
// task 5 dies here freeing task 4 (its parent)
cout << "t5 dies\n";
waitFor5 = false;
};
void P()
{
cout << "t5 runs\n";
Q();
};
void O(int o)
{
task_group t5;
t5.run(&P);
t5.wait();
R(o);
};
void N(int o)
{
// breaks here at the same time as M and Q
InterlockedIncrement(&cc);
while (cc < 4)
{
;
}
R(o);
};
void M(int o)
{
// breaks here at the same time as N and Q
InterlockedIncrement(&cc);
while (cc < 3)
{
;
}
//BP3 - 1 in M, 2 in N, 3 still in J, 4 in O, 5 in Q
DebugBreak();
InterlockedIncrement(&cc);
while (true)
Sleep(500); // for ever
};
void L(int oo)
{
int temp3 = oo;
switch (temp3)
{
case 1:
M(oo);
break;
case 2:
N(oo);
break;
case 4:
O(oo);
break;
default:
throw; //fool3
break;
}
}
void K(int o)
{
// break here at the same time as E and H
InterlockedIncrement(&bb);
EnterCriticalSection(&cs);
while (bb < 3)
{
;
}
LeaveCriticalSection(&cs);
Spin();
//after
L(o);
}
void J(int o)
{
int temp2 = o;
switch (temp2)
{
case 3:
task4.wait();
break;
case 4:
K(o);
break;
default:
throw; //fool2
break;
}
}
static void I(int o)
{
J(o);
}
static void H(int o)
{
// break here at the same time as E and K
InterlockedIncrement(&bb);
EnterCriticalSection(&cs);
while (bb < 3)
{
;
}
LeaveCriticalSection(&cs);
Spin();
//after
L(o);
}
static void G(int o)
{
H(o);
}
static void F(int o)
{
G(o);
}
static void E(int o)
{
// break here at the same time as H and K
while (bb < 2)
{
;
}
//BP2 - 1 in E, 2 in H, 3 in J, 4 in K
Spin(); // for native case only
DebugBreak();
InterlockedIncrement(&bb);
//after
L(o);
}
static void D(int o)
{
E(o);
}
static void C(int o)
{
int temp = o;
InterlockedIncrement(&aa);
while (aa < 4)
{
;
}
if (temp == 1)
{
// BP1 - all tasks in C
DebugBreak();
waitFor1 = false;
}
else
{
while (waitFor1)
{
;
}
}
switch (temp)
{
case 1:
D(o);
break;
case 2:
F(o);
break;
case 3:
case 4:
I(o);
break;
default:
throw; //fool
break;
}
}
static void B(int o)
{
C(o);
}
void A(int o)
{
B(o);
}
int main()
{
InitializeCriticalSection(&cs);
task_group tasks;
task_handle<RunFunc<decltype(A)>> t1(RunFunc<decltype(A)>(A,1));
tasks.run(t1);
task_handle<RunFunc<decltype(A)>> t2(RunFunc<decltype(A)>(A,2));
task2.run(t2);
task_handle<RunFunc<decltype(A)>> t3(RunFunc<decltype(A)>(A,3));
task3.run(t3);
task_handle<RunFunc<decltype(A)>> t4(RunFunc<decltype(A)>(A,4));
task4.run(t4);
getchar();
return 1;
}
On the File menu, click Save All.
-
On the Build menu, click Rebuild Solution.
Notice that there are four calls to Debugger.Break (DebugBreak in the C++ sample) Therefore, you do not have to insert breakpoints; just running the application will cause it to break in the debugger up to four times.
On the Debug menu, click Start Debugging. Wait for the first breakpoint to be hit.
To view the call stack of a single thread
On the Debug menu, point to Windows and then clickThreads. Dock theThreads window at the bottom of Visual Studio.
On the Debug menu, point to Windows and then clickCall Stack. Dock theCall Stack window at the bottom Visual Studio.
Double-click a thread in the Threads window to make it current. Current threads have a yellow arrow. When you change the current thread, its call stack is displayed in theCall Stack window.
To examine the Parallel Stacks window
-
On the Debug menu, point to Windows and then clickParallel Stacks. Make sure thatThreads is selected in the box at the upper-left corner.
By using the Parallel Stacks window, you can view multiple call stacks at the same time in one view. The following illustration shows theParallel Stackswindow above the Call Stack window.
The call stack of the Main thread appears in one box and the call stacks for the other four threads are grouped in another box. Four threads are grouped together because their stack frames share the same method contexts; that is, they are in the same methods:A,B, and C. To view the thread IDs and names of the threads that share the same box, hover over the header (4 Threads). The current thread is displayed in bold, as shown in the following illustration.
The yellow arrow indicates the active stack frame of the current thread. To get more information, hover over it
You can set how much detail to show for the stack frames (Module Names,Parameter Types,Parameter Names,Parameter Values,Line Numbers andByte Offsets) by right-clicking in theCall Stack window.
A blue highlight around a box indicates that the current thread is part of that box. The current thread is also indicated by the bold stack frame in the tooltip. If you double-click the Main thread in the Threads window, you can observe that the blue highlight in the Parallel Stacks window moves accordingly.
To resume execution until the second breakpoint
-
To resume execution until the second breakpoint is hit, on the Debug menu, click Continue. The following illustration shows the thread tree at the second breakpoint.
At the first breakpoint, four threads all went from S.A to S.B to S.C methods. That information is still visible in theParallel Stacks window, but the four threads have progressed further. One of them continued to S.D and then S.E. Another continued to S.F, S.G, and S.H. Two others continued to S.I and S.J, and from there one of them went to S.K and the other continued to non-user External Code.
You can hover over the box header, for example, 1 Thread or2 Threads, to see the thread IDs of the threads. You can hover over stack frames to see thread IDs plus other frame details. The blue highlight indicates the current thread and the yellow arrow indicates the active stack frame of the current thread.
The cloth-threads icon (overlapping blue and red waved lines) indicate the active stack frames of the noncurrent threads. In theCall Stack window, double-click S.B to switch frames. TheParallel Stacks window indicates the current stack frame of the current thread by using a green curved arrow icon.
In the Threads window, switch between threads and observe that the view in theParallel Stacks window is updated.
You can switch to another thread, or to another frame of another thread, by using the shortcut menu in theParallel Stacks window. For example, right-click S.J, point toSwitch To Frame, and then click a command.
Right-click S.C and point to Switch To Frame. One of the commands has a check mark that indicates the stack frame of the current thread. You can switch to that frame of the same thread (just the green arrow will move) or you can switch to the other thread (the blue highlight will also move). The following illustration shows the submenu.
When a method context is associated with just one stack frame, the box header displays1 Thread and you can switch to it by double-clicking. If you double-click a method context that has more than 1 frame associated with it, then the menu automatically pops up. As you hover over the method contexts, notice the black triangle at the right. Clicking that triangle also displays the shortcut menu.
For large applications that have many threads, you may want to focus on just a subset of threads. TheParallel Stacks window can display call stacks only for flagged threads. On the toolbar, click theShow Only Flagged button next to the list box.
Next, in the Threads window, flag threads one by one to see how their call stacks appear in theParallel Stacks window. To flag threads, use the shortcut menu or the first cell of a thread. Click theShow Only Flagged toolbar button again to show all threads.
To resume execution until the third breakpoint
-
To resume execution until the third breakpoint is hit, on the Debug menu, click Continue.
When multiple threads are in the same method but the method was not at the beginning of the call stack, the method appears in different boxes. An example at the current breakpoint is S.L, which has three threads in it and appears in three boxes. Double-click S.L.
Notice that S.L is bold in the other two boxes so that you can see where else it appears. If you want to see which frames call into S.L and which frames it calls, click theToggle Method View button on the toolbar. The following illustration shows the method view of TheParallel Stacks window.
Notice how the diagram pivoted on the selected method and positioned it in its own box in the middle of the view. The callees and callers appear on the top and bottom. Click theToggle Method View button again to leave this mode.
The shortcut menu of the Parallel Stacks window also has the following other items.
Hexadecimal Display toggles the numbers in the tooltips between decimal and hexadecimal.
Symbol Load Information and Symbol Settingsopen the respective dialog boxes.
Go To Source Code and Go To Disassembly navigate in the editor to the selected method.
Show External Code displays all the frames even if they are not in user code. Try it to see the diagram expand to accommodate the additional frames (which may be dimmed because you do not have symbols for them).
When you have large diagrams and you step to the next breakpoint, you may want the view to auto scroll to the active stack frame of the current thread; that is, the thread that hit the breakpoint first. In theParallel Stacks window, make sure that the Auto Scroll to Current Stack Frame button on the toolbar is on.
Before you continue, in the Parallel Stacks window, scroll all the way to the left and all the way down.
To resume execution until the fourth breakpoint
-
To resume execution until the fourth breakpoint is hit, on the Debug menu, click Continue.
Notice how the view autoscrolled into place. Switch threads in the Threads window or switch stack frames in the Call Stack window and notice how the view always autoscrolls to the correct frame. Turn offAuto Scroll to Current Tool Frame option and view the difference.
The Bird's Eye View also helps with large diagrams in theParallel Stacks window. You can see theBird's Eye View by clicking the button between the scroll bars on the lower-right corner of the window, as shown in the following illustration.
You can move the rectangle to quickly pan around the diagram.
Another way to move the diagram in any direction is to click a blank area of the diagram and drag it where you want it.
To zoom in and out of the diagram, press and hold CTRL while you move the mouse wheel. Alternatively, click the Zoom button on the toolbar and then use the Zoom tool.
You can also view the stacks in a top-down direction instead of bottom-up, by clicking theTools menu, clickingOptions, and then select or clear the option under theDebugging node.
Before you continue, on the Debug menu, click Stop Debugging to end execution.
We recommended that you complete the earlier procedures before you continue.
To restart the application until the first breakpoint is hit
On the Debug menu, click Start Debugging and wait for the first breakpoint to be hit.
On the Debug menu, point to Windows and then clickThreads. Dock theThreads window at the bottom of Visual Studio.
On the Debug menu, point to Windows and clickCall Stack. Dock theCall Stack window at the bottom Visual Studio.
Double-click a thread in the Threads window to makes it current. Current threads have the yellow arrow. When you change the current thread, the other windows are updated. Next, we will examine tasks.
-
On the Debug menu, point to Windows and then clickParallel Tasks. The following illustration shows theParallel Tasks window.
For each running Task, you can read its ID, which is returned by the same-named property, the ID and name of the thread that runs it, its location (hovering over that displays a tooltip that has the whole call stack). Also, under theTask column, you can see the method that was passed into the task; in other words, the starting point.
You can sort any column. Notice the sort glyph that indicates the sort column and direction. You can also reorder the columns by dragging them left or right.
The yellow arrow indicates the current task. You can switch tasks by double-clicking a task or by using the shortcut menu. When you switch tasks, the underlying thread becomes current and the other windows are updated.
When you manually switch from one task to another, the yellow arrow moves, but a white arrow still shows the task that caused the debugger to break.
To resume execution until the second breakpoint
-
To resume execution until the second breakpoint is hit, on the Debug menu, click Continue.
Previously, the Status column showed all tasks as Running, but now two of the tasks are Waiting. Tasks can be blocked for many different reasons. In theStatus column, hover over a waiting task to learn why it is blocked. For example, in the following illustration, task 3 is waiting on task 4.
Task 4, in turn, is waiting on a monitor owned by the thread assigned to task 2.
You can flag a task by clicking the flag in the first column of the Parallel Tasks window.
You can use flagging to track tasks between different breakpoints in the same debugging session or to filter for tasks whose call stacks are shown in theParallel Stacks window.
When you used the Parallel Stacks window earlier, you viewed the application threads. View theParallel Stacks window again, but this time view the application tasks. Do this by selectingTasks in the box on the upper left. The following illustration shows the Tasks View.
Threads that are not currently executing tasks are not shown in the Tasks View of theParallel Stacks window. Also, for threads that execute tasks, some of the stack frames that are not relevant to tasks are filtered from the top and bottom of the stack.
View the Parallel Tasks window again. Right-click any column header to see a shortcut menu for the column.
You can use the shortcut menu to add or remove columns. For example, the AppDomain column is not selected; therefore, it is not displayed in the list. ClickParent. TheParent column appears without values for any of the four tasks.
To resume execution until the third breakpoint
-
To resume execution until the third breakpoint is hit, on the Debug menu, click Continue.
A new task, task 5, is now running and task 4 is now waiting. You can see why by hovering over the waiting task in theStatus window. In the Parentcolumn, notice that task 4 is the parent of task 5.
To better visualize the parent-child relationship, right-click the Parent column header and then click Parent Child View. You should see the following illustration.
Notice that task 4 and task 5 are running on the same thread. This information is not displayed in theThreads window; seeing it here is another benefit of theParallel Tasks window. To confirm this, view the Parallel Stacks window. Make sure that you are viewingTasks. Locate tasks 4 and 5 by double-clicking them in theParallel Tasks window. When you do, the blue highlight in theParallel Stacks window is updated. You can also locate tasks 4 and 5 by scanning the tooltips on theParallel Stacks window.
In the Parallel Stacks window, right-click S.P, and then clickGo To Thread. The window switches to Threads View and the corresponding frame is in view. You can see both tasks on the same thread.
This is another benefit of the Tasks View in the Parallel Stacks window, compared to theThreads window.
To resume execution until the fourth breakpoint
-
To resume execution until the third breakpoint is hit, on the Debug menu, click Continue. Click the ID column header to sort by ID. You should see the following illustration.
Because task 5 has completed, it is no longer displayed. If that is not the case on your computer and the deadlock is not shown, step one time by pressing F11.
Task 3 and task 4 are now waiting on each other and are deadlocked. There are also 5 new tasks that are children of task 2 and are now scheduled. Scheduled tasks are tasks that have been started in code but have not run yet. Therefore, theirLocation and Thread Assignment columns are empty.
View the Parallel Stacks window again. The header of each box has a tooltip that shows the thread IDs and names. Switch to Tasks View in theParallel Stacks window. Hover over a header to see the task ID and name, and the status of the task, as shown in the following illustration.
You can group the tasks by column. In the Parallel Tasks window, right-click theStatus column header and then clickGroup by Status. The following illustration shows the Parallel Tasks window grouped by status.
You can also group by any other column. By grouping tasks, you can focus on a subset of tasks. Each collapsible group has a count of the items that are grouped together. You can also quickly flag all items in the group by clicking theFlag button to the right of the Collapse button.
The last feature of the Parallel Tasks window to examine is the shortcut menu that is displayed when you right-click a task.
The shortcut menu displays different commands, depending on the status of the task. The commands may includeCopy,Select All, Hexadecimal Display,Switch to Task, Freeze Assigned Thread,Freeze All Threads But This, andThaw Assigned Thread, andFlag.
You can freeze the underlying thread of a task, or tasks, or you can freeze all threads except the assigned one. A frozen thread is represented in theParallel Tasks window as it is in theThreads window, by a blue pause icon.
This walkthrough demonstrated the Parallel Tasks andParallel Stacks debugger windows. Use these windows on real projects that use multithreaded code. You can examine parallel code written in C++, C#, or Visual Basic.