代码之家  ›  专栏  ›  技术社区  ›  Jorge Córdoba

在多线程代码中,由于性能原因,应该避免什么?

  •  7
  • Jorge Córdoba  · 技术社区  · 16 年前

    我目前正在审查/重构一个多线程应用程序,该应用程序应该是多线程的,以便能够使用所有可用的核心,理论上提供更好/更好的性能(superior是better:p的商业术语)

    编程多线程应用程序时,我应该注意哪些事项?

    我的意思是那些会对性能产生很大影响的事情,甚至可能到了这样的地步:在多线程的情况下,你什么也得不到,但是在设计上的复杂性会使你损失很多。多线程应用程序的大红旗是什么?

    我应该开始质疑这些锁,并寻找一种无锁策略,还是有其他更重要的地方需要亮起警示灯?

    编辑: 我想要的答案类似于Janusz的答案,我希望在代码中查找红色警告,我知道应用程序的性能不如它应该的那样好,我需要知道从哪里开始查找,什么应该让我担心,我应该把我的努力放在哪里。我知道这是一个一般性的问题,但我不能发布整个程序,如果我可以选择代码的一部分,那么我就不需要首先问了。

    我正在使用Delphi7,虽然明年的应用程序将在.NET(c)中移植/重新制作,所以我宁愿听到一些适用于一般实践的评论,如果它们必须特定于这些语言中的任何一种

    12 回复  |  直到 16 年前
        1
  •  6
  •   Zan Lynx    16 年前

    一定要避免的一件事是线程对相同缓存线的大量写访问。

    例如:如果使用一个计数器变量来计算所有线程处理的项目数,这将真正影响性能,因为每当其他CPU写入该变量时,CPU缓存线都必须同步。

        2
  •  5
  •   Janusz Daniel Rindt    16 年前

    降低性能的一件事是让两个线程具有大量硬盘访问权限。硬盘将从为一个线程提供数据跳到另一个线程,两个线程将一直等待磁盘。

        3
  •  5
  •   Rob    16 年前

    锁定时要记住的事情:尽可能短地锁定。例如,不是这样:

    lock(syncObject)
    {
        bool value = askSomeSharedResourceForSomeValue();
        if (value)
            DoSomethingIfTrue();
        else
            DoSomtehingIfFalse();
    }
    

    执行此操作(如果可能):

    bool value = false;  
    
    lock(syncObject)
    {
        value = askSomeSharedResourceForSomeValue();
    }  
    
    if (value)
       DoSomethingIfTrue();
    else
       DoSomtehingIfFalse();
    

    当然,这个例子只有在 DoSomethingIfTrue() DoSomethingIfFalse() 不需要同步,但它说明了这一点:锁定尽可能短的时间,虽然可能不总是提高性能,但它会提高代码的安全性,因为它减少了同步问题的表面积。

    在某些情况下,它会提高性能。长时间保持锁定意味着等待访问某些资源的其他线程将等待更长的时间。

        4
  •  4
  •   Toad    16 年前

    更多的线程比核心线程多,通常意味着程序没有以最佳方式运行。

    因此,产生大量线程的程序通常不是以最佳方式设计的。这个实践的一个好例子是经典的套接字示例,其中每个传入连接都有自己的线程来处理连接。这是一种非常不可扩展的方式。线程越多,操作系统在线程之间进行上下文切换的时间就越多。

        5
  •  3
  •   Kathy Van Stone    16 年前

    你应该先熟悉 Amdahl's law .

    如果你使用Java,我推荐这本书。 Java Concurrency in Practice 但是,它的大部分帮助是特定于Java语言(Java 5或更高版本)。

    一般来说,减少共享内存的数量会增加可能的并行性,对于性能来说,这应该是一个主要考虑因素。

    使用GUI进行线程处理是另一件需要注意的事情,但是看起来它与这个特定的问题不相关。

        6
  •  2
  •   Wim ten Brink    16 年前

    当两个或多个线程共享相同的资源时,会导致性能下降。这可以是同时使用的对象,也可以是同时使用的文件、同时使用的网络或同时使用的处理器。您不能避免这些对共享资源的依赖,但如果可能,请尝试避免共享资源。

        7
  •  1
  •   ChrisW    16 年前

    运行时分析器在多线程应用程序中可能无法正常工作。但是,任何使单线程应用程序变慢的操作都会使多线程应用程序变慢。将应用程序作为单线程应用程序运行,并使用探查器来找出其性能热点(瓶颈)的位置,这可能是一个好主意。

    当它作为多线程应用程序运行时,您可以使用系统的性能监视工具来查看锁是否有问题。假设线程将锁定而不是繁忙等待,那么为多个线程拥有100%的CPU就意味着锁定不是问题。相反,在双处理器机器上,CPU总利用率为50%的情况表明只有一个线程在运行,因此锁定可能是阻止多个并发线程的问题(计算机器中的CPU数量时,请注意多核和超线程)。

    锁不仅存在于代码中,也存在于您使用的API中:例如堆管理器(每当您分配和删除内存时),可能存在于记录器实现中,也可能存在于某些O/S API中,等等。

    我应该开始质疑这些锁并寻找一种无锁策略吗?

    我总是质疑锁,但从来没有使用过无锁策略;相反,我的目标是在必要时使用锁,这样它总是线程安全的,但不会死锁,并确保获取锁的时间很短(例如,推送或弹出线程安全队列上的指针所需的时间不超过),所以线程被阻塞的最长时间与执行有用工作的时间相比微不足道。

        8
  •  1
  •   Steve    16 年前

    您没有提到您使用的语言,所以我将对锁定做一个一般性的声明。锁是相当昂贵的,尤其是许多语言固有的幼稚锁。在许多情况下,您正在读取共享变量(而不是写入)。只要不与写同时进行,阅读就是安全的。但是,您仍然需要将其锁定。这种锁定最幼稚的形式是将读和写视为同一类型的操作,限制从其他读和写访问共享变量。读/写锁可以显著提高性能。一个作家,无限的读者。在我工作过的一个应用程序中,当切换到这个结构时,我看到了35%的性能改进。如果您在.NET中工作,正确的锁是readerwriterlockslim。

        9
  •  1
  •   Ernelli    16 年前

    如果是服务器应用程序,我建议在同一进程中运行多个进程,而不是多个线程。

    在一台机器上将工作划分为多个进程的好处是,当需要比单个服务器能够提供更多性能时,很容易增加服务器的数量。

    您还可以降低复杂的多线程应用程序所涉及的风险,其中死锁、瓶颈等会降低总体性能。

    在负载平衡和分布式队列处理方面,有一些商业框架可以简化服务器软件的开发,但是与多线程应用程序中通常遇到的情况相比,开发自己的负载共享基础架构并没有那么复杂。

        10
  •  1
  •   ChrisW    16 年前

    我用的是Delphi7

    那么,您可能在显式或隐式地使用COM对象;如果使用,则COM对象在线程方面有其自身的复杂性和限制: Processes, Threads, and Apartments .

        11
  •  0
  •   Tamara Wijsman    16 年前

    您应该首先获得一个工具来监视特定于您的语言、框架和IDE的线程。你自己的记录器也可能做得很好(恢复时间,睡眠时间+持续时间)。从那里,您可以检查执行不多或等待时间太长的执行不良线程,从而使它们等待的事件尽可能早地发生。

    由于您想同时使用两个核心,您应该使用一个工具来检查核心的使用情况,该工具可以只为您的应用程序在两个核心上绘制处理器的使用情况图表,或者确保您的计算机尽可能空闲。

    此外,您应该对应用程序进行概要分析,以确保在线程中执行的操作是有效的,但是要注意提前优化。如果线程本身执行得不好,那么就没有必要优化您的多处理。

    寻找一个无锁策略可以帮助很多,但并不总是能够让您的应用程序以无锁的方式执行。

        12
  •  0
  •   David    16 年前

    线程并不总是等于性能。

    与其他操作系统相比,某些操作系统的情况要好得多,但是如果您可以睡眠或在收到信号之前放弃时间……或者不为几乎所有的事情启动新的过程,那么您就可以避免将应用程序陷入上下文切换的困境。