java线程 调试_了解如何调试多线程应用程序 - Visual Studio | Microsoft Docs

开始调试多线程应用程序(C#、Visual Basic、C++)Get started debugging multithreaded applications (C#, Visual Basic, C++)

02/14/2020

本文内容

Visual Studio 提供多种工具和用户界面元素,用于调试多线程应用程序。Visual Studio provides several tools and user interface elements to help you debug multithreaded applications. 本教程演示如何使用线程标记、“并行堆栈” 窗口、“并行监视” 窗口、条件断点、筛选器断点。This tutorial shows how to use thread markers, the Parallel Stacks window, the Parallel Watch window, conditional breakpoints, and filter breakpoints. 完成本教程只需数分钟,然后你就会熟悉用于调试多线程应用程序的功能。Completing this tutorial will familiarize you with Visual Studio features for debugging multithreaded applications.

下面两个主题额外介绍了如何使用其他多线程调试工具:These two topics provide additional information on using other multithreaded debugging tools:

若要使用“调试位置” 工具栏和“线程” 窗口,请参阅演练:调试多线程应用程序。To use the Debug Location toolbar and the Threads window, see Walkthrough: Debug a multithreaded application.

有关使用 Task(托管代码)和并发运行时 (C++) 的示例,请参阅演练:调试并行应用程序。For a sample that uses Task (managed code) and the concurrency runtime (C++), see Walkthrough: Debug a parallel application. 有关适用于大多数多线程应用程序类型的常规调试技巧,请阅读该主题和本主题。For general debugging tips that apply to most multithreaded application types, read both that topic and this one.

首先需要一个多线程应用程序项目。You'll first need a multithreaded application project. 示例如下。An example follows.

创建一个多线程应用项目Create a multithreaded app project

打开 Visual Studio 并创建一个新项目。Open Visual Studio and create a new project.

如果开始窗口未打开,请选择“文件”>“开始窗口” 。If the start window is not open, choose File > Start Window.

在“开始”窗口上,选择“创建新项目” 。On the start window, choose Create a new project.

在“创建新项目”窗口的搜索框中输入或键入“控制台” 。On the Create a new project window, enter or type console in the search box. 接下来,从“语言”列表中选择“C#”、“C++”或“Visual Basic”,然后从“平台”列表中选择“Windows” 。Next, choose C#, C++, or Visual Basic from the Language list, and then choose Windows from the Platform list.

应用语言和平台筛选器之后,选择“控制台应用(.NET Core)”模板(对于 C++,选择“控制台应用”模板),然后选择“下一步” 。After you apply the language and platform filters, choose the Console App (.NET Core) or, for C++, Console App template, and then choose Next.

备注

如果没有看到正确的模板,请转到“工具” > “获取工具和功能...”,这会打开 Visual Studio 安装程序 。If you don't see the correct template, go to Tools > Get Tools and Features..., which opens the Visual Studio Installer. 选择“.NET 桌面开发”或“使用 C++ 的桌面开发”工作负载,然后选择“修改” 。Choose the .NET desktop development or Desktop development with C++ workload, then choose Modify.

在“配置新项目”窗口中,在“项目名称”框中键入或输入 MyThreadWalkthroughApp。In the Configure your new project window, type or enter MyThreadWalkthroughApp in the Project name box. 然后,选择“创建” 。Then, choose Create.

从顶部菜单栏中选择“文件” > “新建” > “项目”。From the top menu bar, choose File > New > Project. 在“新建项目”对话框的左窗格中,选择以下内容:In the left pane of the New project dialog box, choose the following:

对于 C# 应用,在“Visual C#”下,选择“Windows 桌面”,然后在中间窗格中选择“控制台应用(.NET Framework)” 。For a C# app, under Visual C#, choose Windows Desktop, and then in the middle pane choose Console App (.NET Framework).

对于 Visual Basic 应用,在“Visual Basic”下,选择“Windows 桌面”,然后在中间窗格中选择“控制台应用(.NET Framework)” 。For a Visual Basic app, under Visual Basic, choose Windows Desktop, and then in the middle pane choose Console App (.NET Framework).

对于 C++ 应用,在“Visual C++”下,选择“Windows 桌面”,然后选择“Windows 控制台应用程序”。For a C++ app, under Visual C++, choose Windows Desktop,, and then choose Windows Console Application.

如果没有看到“控制台应用(.NET Core)”项目模板,或者对于 C++,没有看到“控制台应用”项目模板,请转到“工具” > “获取工具和功能...”,这会打开 Visual Studio 安装程序 。If you don't see the Console App (.NET Core) or, for C++, the Console App project template, go to Tools > Get Tools and Features..., which opens the Visual Studio Installer. 选择“.NET 桌面开发”或“使用 C++ 的桌面开发”工作负载,然后选择“修改” 。Choose the .NET desktop development or Desktop development with C++ workload, then choose Modify.

然后,键入名称(如 MyThreadWalkthroughApp),并单击“确定”。Then, type a name like MyThreadWalkthroughApp and click OK.

选择“确定”。Select OK.

新的控制台项目随即显示。A new console project appears. 创建该项目后,将显示源文件。After the project has been created, a source file appears. 根据所选语言,源文件名称可能是 Program.cs、MyThreadWalkthroughApp.cpp 或 Module1.vb 。Depending on the language you have chosen, the source file might be called Program.cs, MyThreadWalkthroughApp.cpp, or Module1.vb.

删除出现在源文件中的代码,将其替换为下面列出的相应示例代码。Delete the code that appears in the source file and replace it with the appropriate example code listing below.

using System;

using System.Threading;

public class ServerClass

{

static int count = 0;

// The method that will be called when the thread is started.

public void InstanceMethod()

{

Console.WriteLine(

"ServerClass.InstanceMethod is running on another thread.");

int data = count++;

// Pause for a moment to provide a delay to make

// threads more apparent.

Thread.Sleep(3000);

Console.WriteLine(

"The instance method called by the worker thread has ended. " + data);

}

}

public class Simple

{

public static void Main()

{

for (int i = 0; i < 10; i++)

{

CreateThreads();

}

}

public static void CreateThreads()

{

ServerClass serverObject = new ServerClass();

Thread InstanceCaller = new Thread(new ThreadStart(serverObject.InstanceMethod));

// Start the thread.

InstanceCaller.Start();

Console.WriteLine("The Main() thread calls this after "

+ "starting the new InstanceCaller thread.");

}

}

// #include "pch.h" // Use with pre-compiled header

#include

#include

#include

#include

int count = 0;

void doSomeWork() {

std::cout << "The doSomeWork function is running on another thread." << std::endl;

int data = count++;

// Pause for a moment to provide a delay to make

// threads more apparent.

std::this_thread::sleep_for(std::chrono::seconds(3));

std::string str = std::to_string(data);

std::cout << "The function called by the worker thread has ended. " + str<< std::endl;

}

int main() {

std::vector<:thread> threads;

for (int i = 0; i < 10; ++i) {

threads.push_back(std::thread(doSomeWork));

std::cout << "The Main() thread calls this after starting the new thread" << std::endl;

}

for (auto& thread : threads) {

thread.join();

}

return 0;

}

Imports System.Threading

Public Class ServerClass

' The method that will be called when the thread is started.

Public count = 0

Public Sub InstanceMethod()

Console.WriteLine(

"ServerClass.InstanceMethod is running on another thread.")

Dim data = count + 1

' Pause for a moment to provide a delay to make

' threads more apparent.

Thread.Sleep(3000)

Console.WriteLine(

"The instance method called by the worker thread has ended. " + data)

End Sub

End Class

Public Class Simple

Public Shared Sub Main()

Dim ts As New ThreadStarter

For index = 1 To 10

ts.CreateThreads()

Next

End Sub

End Class

Public Class ThreadStarter

Public Sub CreateThreads()

Dim serverObject As New ServerClass()

' Create the thread object, passing in the

' serverObject.InstanceMethod method using a

' ThreadStart delegate.

Dim InstanceCaller As New Thread(AddressOf serverObject.InstanceMethod)

' Start the thread.

InstanceCaller.Start()

Console.WriteLine("The Main() thread calls this after " _

+ "starting the new InstanceCaller thread.")

End Sub

End Class

在“文件” 菜单上,单击“全部保存” 。On the File menu, select Save All.

(仅适用于 Visual Basic)在“解决方案资源管理器”(右窗格)中,右键单击项目节点,然后选择“属性” 。(Visual Basic only) In Solution Explorer (right pane), right-click the project node, choose Properties. 在“应用程序” 选项卡下,将“启动对象” 更改为“简单” 。Under the Application tab, change the Startup object to Simple.

调试多线程应用Debug the multithreaded app

在源代码编辑器中,查找以下代码片段之一:In the source code editor, look for one of the following code snippets:

Thread.Sleep(3000);

Console.WriteLine();

std::this_thread::sleep_for(std::chrono::seconds(3));

std::cout << "The function called by the worker thread has ended." << std::endl;

Thread.Sleep(3000)

Console.WriteLine()

在 Thread.Sleep 或 std::this_thread::sleep_for 语句的左滚动条槽中单击左键,以插入新的断点。Left-click in the left gutter of the Thread.Sleep or std::this_thread::sleep_for statement to insert a new breakpoint.

在滚动条槽中,红色圆圈表示已在该位置设置了一个断点。In the gutter, a red circle indicates that a breakpoint is set at this location.

在“调试” 菜单上,单击“开始调试(F5)” 。On the Debug menu, select Start Debugging (F5).

Visual Studio 将生成该解决方案,应用在附加了调试器的情况下开始运行,然后在断点处停止。Visual Studio builds the solution, the app starts to run with the debugger attached, and then the app stops at the breakpoint.

在源代码编辑器中,找到包含断点的行。In the source code editor, locate the line that contains the breakpoint.

发现线程标记Discover the thread marker

在调试工具栏中,选择“在源中显示线程” 按钮

c675db2ab1348d734b145e6677651e53.png。In the Debug Toolbar, select the Show Threads in Source button

c675db2ab1348d734b145e6677651e53.png.

按一下 F11 使调试器前进一个代码行。Press F11 once to advance the debugger one line of code.

查看窗口左侧的滚动条槽。Look at the gutter on the left side of the window. 在此行中,你会看到线程标记 图标

0c1583bc17781cd4646922dd1e62043e.png,类似于一条双绞线。On this line, you will see a thread marker icon

0c1583bc17781cd4646922dd1e62043e.png that resembles two twisted threads. 线程标记指示线程在此位置停止。The thread marker indicates that a thread is stopped at this location.

线程标记可以被断点部分隐藏。A thread marker may be partially concealed by a breakpoint.

将指针悬停在线程标记上。Hover the pointer over the thread marker. 此时会出现一个数据提示,告知你每个已停止线程的名称和线程 ID 号。A DataTip appears telling you the name and thread ID number for each stopped thread. 在这种情况下,名称可能是 。In this case, the name is probably .

选择线程标记,以查看快捷菜单上的可用选项。Select the thread marker to see the available options on the shortcut menu.

查看线程位置View the thread locations

在“并行堆栈” 窗口中,可以在“线程”视图和“任务”视图(适用于基于任务的编程)之间进行切换,并且可以查看每个线程的调用堆栈信息。In the Parallel Stacks window, you can switch between a Threads view and (for task-based programming) Tasks view, and you can view call stack information for each thread. 在此应用中,我们可以使用“线程”视图。In this app, we can use the Threads view.

通过选择“调试” > “窗口” > “并行堆栈” ,打开“并行堆栈” 窗口。Open the Parallel Stacks window by choosing Debug > Windows > Parallel Stacks. 此时会看到如下所示的内容。You should see something similar to the following. 确切信息取决于每个线程的当前位置、硬件以及编程语言。The exact information will differ depending on the current location of each thread, your hardware, and your programming language.

2e354e0c495b9b4e0463888cde70078a.png2e354e0c495b9b4e0463888cde70078a.png

在此示例中,从左到右会看到托管代码的以下信息:In this example, from left to right we see this information for managed code:

主线程(左侧)已在 Thread.Start 处停止,停止点由线程标记图标

0c1583bc17781cd4646922dd1e62043e.png 表示。The Main thread (left side) has stopped on Thread.Start, where the stop point is indicated by the thread marker icon

0c1583bc17781cd4646922dd1e62043e.png.

两个线程已进入 ServerClass.InstanceMethod,其中一个线程是当前线程(黄色箭头),另一个线程已停止在 Thread.Sleep 中。Two threads have entered the ServerClass.InstanceMethod, one of which is the current thread (yellow arrow), while the other thread has stopped in Thread.Sleep.

新线程(右侧)也已启动,但是停止在 ThreadHelper.ThreadStart 上。A new thread (on the right) is also starting but is stopped on ThreadHelper.ThreadStart.

右键单击“并行堆栈” 窗口中的条目,查看快捷菜单上的可用选项。Right-click entries in the Parallel Stacks window to see the available options on the shortcut menu.

可以通过这些右键单击菜单执行各种操作,但在本教程中,我们将在“并行监视” 窗口中展示更多这些细节(后续部分)。You can take various actions from these right-click menus, but for this tutorial we will show more of these details in the Parallel Watch window (next sections).

备注

若要查看包含每个线程的信息的列表视图,请改用“线程” 窗口。To see a list view with information on each thread, use the Threads window instead.

对变量设置监视Set a watch on a variable

通过选择“调试” > “窗口” > “并行监视” > “并行监视 1” ,打开“并行监视”窗口 。Open the Parallel Watch window by selecting Debug > Windows > Parallel Watch > Parallel Watch 1.

选择 文本所在的单元格(或第 4 列中的空标头单元格),输入 data。Select the cell where you see the text (or the empty header cell in the 4th column) and enter data.

窗口中会显示每个线程的数据变量的值。The values for the data variable for each thread appear in the window.

选择 文本所在的单元格(或第 5 列中的空标头单元格),输入 count。Select the cell where you see the text (or the empty header cell in the 5th column) and enter count.

窗口中会显示每个线程的 count 变量的值。The values for the count variable for each thread appear in the window. 如果看不到这么多的信息,请尝试按 F11 几次以继续在调试器中执行线程。If you don't see this much information yet, try pressing F11 a few times to advance the execution of the threads in the debugger.

5c88c6713956aa6fa3ee59f69f017735.png5c88c6713956aa6fa3ee59f69f017735.png

右键单击窗口中的某一行以查看可用选项。Right-click on one of the rows in the window to see the available options.

标记线程和取消标记线程Flag and unflag threads

可以通过标记线程来追踪重要的线程,并忽略其它线程。You can flag threads to keep track of important threads and ignore the other threads.

在“并行监视” 窗口中,按住 Shift 键并选择多行。In the Parallel Watch window, hold down the Shift key and select multiple rows.

右键单击并选择“标记” 。Right-click and select Flag.

系统会标记所有选定的线程。All the selected threads are flagged. 现在,可以进行筛选以仅显示标记的线程。Now, you can filter to show only flagged threads.

在“并行监视” 窗口中,选择“仅显示标记的线程” 按钮

861ca57346d97e6d440eba1cdc4cff85.png。In the Parallel Watch window, select the Show Only Flagged Threads button

861ca57346d97e6d440eba1cdc4cff85.png.

列表中仅显示标记的线程。Only the flagged threads appear in the list.

提示

在标记一些线程后,可以右键单击代码编辑器中的代码行,然后选择“将标记的线程运行到光标处” 。After you have flagged some threads, you can right-click a line of code in the code editor and choose Run Flagged Threads to Cursor. 请确保选择所有已标记的线程将达到的代码。Make sure to choose code that all flagged threads will reach. Visual Studio 将在选择的代码行处暂停线程,这样就可以通过冻结和解冻线程更容易地控制执行顺序。Visual Studio will pause threads on the selected line of code, making it easier to control the order of execution by freezing and thawing threads.

再次选择“仅显示标记的线程” 按钮,以切换回“显示全部线程” 模式。Select the Show Only Flagged Threads button again to toggle back to Show All Threads mode.

若要取消标记线程,请在“并行监视” 窗口右键单击一个或多个已标记线程,然后选择“取消标记” 。To unflag threads, right-click one or more flagged threads in the Parallel Watch window and select Unflag.

冻结和解冻线程执行Freeze and thaw thread execution

提示

可以通过冻结和解冻(暂停和恢复)线程来控制线程执行工作的顺序。You can freeze and thaw (suspend and resume) threads to control the order in which threads perform work. 这有助于解决并发问题,例如死锁和争用条件。This can help you resolve concurrency issues such as deadlocks and race conditions.

在“并行监视” 窗口中,在选中所有行的情况下,右键单击并选择“冻结” 。In the Parallel Watch window, with all the rows selected, right-click and select Freeze.

在第二个列中,每个行出现一个暂停图标。In the second column, a pause icon appears for each row. 暂停图标指示该线程已冻结。The pause icon indicates that the thread is frozen.

仅选择一行,取消选中其他行。Deselect all other rows by selecting one row only.

右键单击某行并选择“解冻” 。Right-click a row and select Thaw.

暂停图标在此行上消失,表明线程已不再被冻结。The pause icon goes away on this row, indicating that the thread is no longer frozen.

切换到代码编辑器,按 F11。Switch to the code editor and press F11. 仅运行未冻结的线程。Only the unfrozen thread runs.

应用还实例化某些新线程。The app may also instantiate some new threads. 任何新线程均处于未标记状态,不会被冻结。Any new threads are unflagged and are not frozen.

使用条件断点跟踪单个线程Follow a single thread with conditional breakpoints

可以在调试器中对单线程的执行情况进行跟踪。It can be helpful to follow the execution of a single thread in the debugger. 一种方法是冻结不感兴趣的线程。One way to do that is by freezing threads that you are not interested in. 在某些情况下,可能需要在不冻结其它线程的情况下跟踪单个线程,例如重现特定 Bug。In some scenarios you may need to follow a single thread without freezing other threads, for example to reproduce a particular bug. 若要在不冻结其他线程的情况下跟踪某个线程,必须避免在不感兴趣的线程上中断。To follow a thread without freezing other threads, you must avoid breaking into code except on the thread that you are interested in. 这可以通过设置条件断点来实现。You can do this by setting a conditional breakpoint.

可以根据不同的条件(例如,线程名称或线程 ID)来设置断点。You can set breakpoints on different conditions, such as the thread name or the thread ID. 如果你知道数据对于每个线程都是唯一的,则可根据该数据设置条件断点。It may be helpful is to set the condition on data that you know is unique to each thread. 这是常见的调试场景,即相比于特定的线程,你对某些特定的数据值更感兴趣。This is a common debugging scenario, in which you are more interested in some particular data value than in any particular thread.

右键单击先前创建的断点,然后选择“条件” 。Right-click the breakpoint you previously created and select Conditions.

在“断点设置” 窗口中,输入 data == 5 作为条件表达式。In the Breakpoint Settings window, enter data == 5 for the conditional expression.

6311ae39c407cacd225acba9fa5efa67.png6311ae39c407cacd225acba9fa5efa67.png

提示

如果对特定的线程更感兴趣,则请使用线程名称或线程 ID 作为条件。If you are more interested in a specific thread, then use a thread name or thread ID for the condition. 若要在“断点设置” 窗口中执行此操作,请选择“筛选器” 而不是“条件表达式” ,并按照筛选器提示操作。To do this in the Breakpoint Settings window, select Filter instead of Conditional expression, and follow the filter tips. 可能需要在应用代码中指定线程名称,因为线程 ID 在重启调试器时会更改。You may want to name your threads in your app code, as threads IDs change when you restart the debugger.

关闭“断点设置” 窗口。Close the Breakpoint Settings window.

选择重启

c30d0866851565c1d9d25261459d94fe.png 按钮以重启调试会话。Select the Restart

c30d0866851565c1d9d25261459d94fe.png button to restart your debugging session.

将在数据变量的值为 5 的线程中,中断代码执行。You'll break into code on the thread where the data variable's value is 5. 请在“并行监视” 窗口中,寻找表示当前调试器上下文的黄色箭头。In the Parallel Watch window, look for the yellow arrow indicating the current debugger context.

现在,可以逐过程执行代码 (F10) 和单步执行代码 (F11),并跟踪单个线程的执行情况。Now, you can step over code (F10) and step into code (F11) and follow the execution of the single thread.

只要断点条件对于线程是唯一的,并且调试器不会在其他线程上命中其他任何断点(你可能需要禁用它们),你就可以逐过程执行代码和单步执行代码,而无需切换到其他线程。So long as the breakpoint condition is unique to the thread, and the debugger doesn't hit any other breakpoints on other threads (you may need to disable them), you can step over code and step into code without switching to other threads.

备注

调试器前进时,所有线程都将运行。When you advance the debugger, all threads will run. 但是,调试器不会中断其他线程上的代码,除非其中一个线程命中断点。However, the debugger won't break into code on other threads unless one of the other threads hits a breakpoint.

请参阅See also

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
IntelliJ是一款面向Java开发的集成开发环境(IDE),提供了许多高级的debug技巧,用于帮助程序员更好地调试代码。以下是其中一些重要的高级debug技巧: 1. 条件断点:在断点处添加条件,只有当该条件满足时,程序才会停止执行。这可以帮助我们找到特定的问题或者只在满足某些条件时进行调试。 2. 日志断点:在代码中设置一个断点,但是不会停止程序的执行。相反,它会在执行时生成一个日志消息,可以帮助我们更好地了解程序的执行流程。 3. 远程调试:IntelliJ可以通过远程调试功能连接到远程服务器或者其他设备上的程序。这样,我们可以在本地IDE中进行断点调试,以便更好地定位和解决问题。 4. 多线程调试:在多线程程序中调试是一项具有挑战性的任务。IntelliJ提供了一些有用的功能,如线程窗口、锁对象监视和线程停止等,可以帮助我们更好地理解和调试多线程代码。 5. 内存探查器:IntelliJ还内置了内存探查器,可以显示程序在运行时的内存使用情况,包括内存泄漏和不合理的内存分配等问题。这可以帮助我们优化程序的内存使用。 6. 调试复杂表达式:在调试过程中,有时候我们需要查看和计算一些复杂的表达式的值。IntelliJ提供了一个“表达式查看器”,可以在调试过程中动态评估和显示表达式的结果。 总的来说,IntelliJ的高级debug技巧可以帮助程序员更好地理解和修复代码中的问题。通过合理利用这些技巧,我们可以更快速、高效地调试代码,并提高开发效率。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值