并发可视化工具可帮助开发人员可视化多线程应用程序的行为。 此工具包含多线程应用程序的常见模式库,这些模式的行为不佳。 该库包括通过工具公开的典型和可识别的视觉模式,以及每个模式表示的行为的说明、该行为可能的结果,以及解决它的最常用方法。
锁争用与串行化执行
有时,尽管应用程序具有多个线程,但并行化应用程序仍会持续串行执行,并且计算机具有足够数量的逻辑核心。 第一个症状是多线程性能不佳,甚至比串行实现慢一点。 在“线程视图”中,看不到并行运行的多个线程;相反,你会看到在任何时候只执行一个线程。 此时,如果单击线程中的一个同步段,可以看到阻塞线程的调用堆栈(阻塞调用堆栈)以及解除阻塞条件的线程的调用堆栈(解除阻塞调用堆栈)。 此外,如果您在分析的进程中发现了取消阻止调用堆栈,则会显示一个 Thread-Ready 连接器。 从这一点开始,你可以通过阻止和取消阻止调用堆栈导航到你的代码,从而更深入地分析序列化的原因。
如下图所示,并发可视化工具还可以在 CPU 使用率视图中公开此症状,其中,尽管存在多个线程,但应用程序只使用一个逻辑核心。
有关详细信息,请参阅 MSDN 杂志文章 “线程性能 - Visual Studio 中的资源争用并发分析”从问题开始“部分。
工作负荷分布不均匀
当在应用程序中的多个并行线程之间发生不规则的工作分布时,每个线程完成其工作时会出现典型的楼梯模式,如上图中所示。 并发可视化工具通常显示每个并发线程的非常接近的开始时间。 但是,这些线程通常以不规则的方式结束,而不是同时结束。 此模式表示一组并行线程之间的工作分布不规则,这可能导致性能下降。 此类问题的最佳方法是重新评估工作在并行线程之间划分的算法。
如下图所示,并发可视化工具还可以在 CPU 使用率视图中公开此症状,作为 CPU 利用率的逐步下降。
过度订阅
对于过度订阅,进程中的活动线程数大于系统上可用的逻辑核心数。 上图显示了过度订阅的结果,在所有活动线程中具有明显的抢占带。 此外,图例说明显示了在抢占过程中花费了大量时间(在本示例中占 84%)。 这可能表示进程要求系统执行比逻辑核心数更多的并发线程。 但是,这可能还表示系统上的其他进程正在使用假定可用于此过程的资源。
评估此问题时,应考虑以下事项:
整个系统可能会过度订阅。 请考虑系统上的其他进程可能抢占线程。 当你在线程视图中悬停在抢占段上时,工具提示将识别被抢占线程的线程和进程。 在您的进程被抢占的整个期间,可能并非一直是同一个进程在执行,但它可以提供一个线索,指出到底是什么导致了针对您进程的抢占压力。
评估流程如何在此工作阶段确定执行的适当线程数量。 如果进程直接计算活动并行线程数,请考虑修改该算法以更好地考虑系统上可用的逻辑核心数。 如果使用并发运行时、任务并行库或 PLINQ,这些库将执行计算线程数的工作。
低效输入/输出
过度使用或滥用 I/O 是应用程序中效率低下的共同原因。 请考虑上图。 可见时间线配置文件显示 I/O 消耗 44% 的可见线程时间。 时间线表明,被分析的应用程序常被大量 I/O 阻塞。 若要查看有关 I/O 类型以及程序被阻止的位置的详细信息,请放大有问题的区域,检查可见的时间线配置文件,然后单击特定的 I/O 块以查看当前调用堆栈。
锁定车队
当应用程序以先到先得的顺序获取锁,而锁的到达率高于获取率时,就会发生锁队列。 这两个条件的组合会导致锁请求开始积压。 解决此问题的一种方法是使用“不公平”锁,即当锁处于未锁状态时,第一个找到它的线程可以获取访问权。 上图显示了此车队行为。 若要解决此问题,请尝试降低同步对象的竞争,并尝试采用不公平锁。