【问题标题】:C# - Improving a Multi-Threaded Application DesignC# - 改进多线程应用程序设计
【发布时间】:2010-07-07 08:15:40
【问题描述】:

规格

  • C# 分布式应用程序。
  • 客户端/服务器设计。
  • 客户端(Winforms)、服务器(Windows 服务)、通过 .Net Remoting 进行通信。
  • 以下问题与应用程序的服务器端有关。
  • (编辑) 应用程序的服务器端 在具有 8 核12Gb 的服务器上运行拉姆
  • (编辑) 由于许多其他服务正在运行,此服务器的 CPU 始终达到 80% 使用率 左右在同一台服务器上运行。

场景

  • 我继承了一个大型遗留应用程序。
  • 它执行大量任务,其中一些是独立的,而另一些则不是。
  • 此应用程序的当前设计涉及创建 14 个线程,每个线程运行 1 个任务或多个任务。
  • 问题是我感觉这个设计元素对性能有影响。

代码示例 - 每个类/线程的设计和运行方式

public class ManageThreads
{
    private Thread doStuffThread = null;

    //Inside the constructor EVERY thread is instantiated and run.
    //(I am aware that this example only shows the use of 1 thread).
    public ManageThreads()
    {
       doStuffThread = new Thread(new ThreadStart(DoSomeStuff.Instance.Start));
       doStuffThread.Start();

       //Instantiate and run another thread.....
       //Instantiate and run another thread.....
       //Instantiate and run another thread.....etc.
    }

}

public class DoSomeStuff
{
    void Start()
    { 
        while(true)
        {
          //Repeatedly do some tasks.....

          Thread.Sleep(5000);
        }
     }   
 }

想法

  • 我想做的是保留现有代码,但修改它的运行方式。
  • 我曾考虑使用 线程池 来解决此问题,但鉴于当前的架构,我不确定如何去做。

问题

  • 当前的设计会显着影响性能吗?
  • 我是否可以在不改变底层功能但稍微改变设计的情况下提高此应用程序的性能?
  • 谁能推荐任何东西/建议我以正确的方式改进它?

非常感谢您的帮助。

【问题讨论】:

  • 如果你有超线程和四核CPU,就像有8个逻辑处理器,你应该ThreadPool.SetMaxThreads=7。如果工作是reactive,您的DoSomeStuff 中可能会有一些if (shouldWork) work,并且您可能在应用程序的某个位置设置shouldWork=true(伪代码)。你可以做ThreadPool.QueueUserWorkItem,而不是shouldWork=true。它可能需要一个包装器。
  • 我看到了您的编辑,那么您可能有 16 个逻辑 CPU 可供您使用,因此上下文切换不应该发生太多(除非您已经在运行许多线程)。在您的应用程序的ThreadPool 版本中,您可能不会看到太大的性能差异(分析您的应用程序以识别您的性能问题)。

标签: c# design-patterns architecture multithreading


【解决方案1】:

“我感觉这个设计元素对性能有影响。”

不要猜测,拿出分析器并测量发生了什么。收集一些关于应用程序花费时间的经验统计数据,然后您可以查看关键点在哪里。

如果花在创建线程上的时间是您最头疼的问题,那么迁移到线程池可能是正确的答案,但如果没有一些取证分析,您将无法得知。

从您发布的小 sn-p 看来,这 14 个线程的寿命相当长,在它们的生命周期中做了很多事情,所以我怀疑这实际上不是问题,但没有足够的信息您的帖子可以对此做出明确的决定。

【讨论】:

  • 所以你的意思是即使有 14 个线程在不断运行,但类中的实际代码可能很糟糕,从而导致应用程序运行缓慢?
  • 完全有可能。请注意,如果它们都有 Thread.Sleep(5000),它们也不会持续运行。
【解决方案2】:

如果您的线程都在工作,并且您的活动线程比处理器多,那么您将花费时间进行上下文切换。

如果您有一个双核处理器,不要指望通过 4 个以上的活动工作线程获得出色的性能。

因此,除非您有一个可以管理它的处理器,否则启动 14 个都在工作的线程是个坏主意。物理处理器架构和功能集对此有很大影响。我的观点是,线程池将有助于管理上下文切换,但一次启动 14 个繁忙线程总是会降低性能......您可能会通过简单地按顺序执行线程来获得更快的性能。显然这是一个很大的声明,所以可能不是真的,但你明白了要点。

因此使用线程池和分析器来确定线程池可用的最佳线程数。

在大多数情况下,当人们使用线程池时,很多线程大部分时间什么都不做,或者线程处于休眠/阻塞状态,而一些缓慢的操作或外部依赖等待响应。

考虑使用异步模式,以便您可以从线程中获取有关进度的信息。

在双核处理器上,如果线程 100% 都在工作,我会犹豫是否使用超过 3 个线程

【讨论】:

  • - 我在问题的开头添加了一些有关服务器的信息。
  • 我发现的一个粗略的经验法则是,每个处理器可以处理大约 2 个线程......但这是特定于我在家里使用的核心双核架构,我已经测试过。你真的需要做实验......没有比经验证据更好的了 - 线程池几乎就是为此而设计的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-05-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多