我应该使用线程或任务 - 多客户端模拟

时间:2012-04-05 18:08:36

标签: c# multithreading task-parallel-library simulation

我正在编写一个客户端模拟程序,其中所有模拟客户端都针对服务器运行一些预定义的例程 - 这是一个运行在azure中的Web服务器,有四个实例。

所有模拟客户端在连接到服务器后运行相同的例程。

我想在任何时候使用我的程序模拟300到800个客户。

我的问题是: 我应该创建N个客户端类实例并在N个不同的线程中运行它们吗? OR

我应该使用任务库来做这些事吗?

4 个答案:

答案 0 :(得分:21)

你当然不应该创建800个帖子。

我们在这里退后一步。您有一个名为“服务器”的设备,它从“客户端”接收“请求”并将“响应”发送回这些客户端。我们假设这些请求是邮局提供的纸张,而回复是包含书籍的方框,也是由邮局提供的。

您希望模拟 800个客户端以测试服务器。

让我们假设一个线程是一个人而一个处理器就是一个主席。一个人只能坐在椅子上做工作。

创建800个线程相当于外出和雇佣800人,并支付每个人向服务器发送一封信。但是你只有四把椅子,所以那800人必须轮流使用椅子。

在现实生活中,这将是一个荒唐的解决方案。像人一样,线程非常昂贵。您应该尽量减少创建的线程数。

那么,您是否应该通过任务工厂创建800个任务并让TPL为您进行并行化?

不,你也不应该这样做。 TPL有一群人(线程)可以从中抽取,它会尝试安排事情,以便工资单上没有人可以坐在椅子上。但是你的任务不是“主持” - - 人们将坐在椅子上,将请求发送到服务器,然后在等待响应回来时离开椅子。 当他们在等待时,TPL现在必须雇用更多人来为其他任务提供服务。

点击Web服务器是I / O绑定的; 您应该只为CPU绑定的任务创建线程池任务。

正确的解决方案是雇用两个人。

一个人 - “I / O完成线程” - 除了删除邮箱中的请求并检查传入的包之外什么都不做。另一个人 - “模拟”人 - 计算出模拟800个客户的正确“时间表”。模拟人员制定计划,然后进入睡眠状态。当需要向服务器发送另一个请求时,她会醒来。当她醒来时,她告诉I / O完成线程将此信件丢弃在邮箱中,并在响应进入时将其唤醒。然后她回到睡眠状态,直到发送另一个请求或响应为止需要进行验证。

你应该做的是(1)获得C#5的beta版本并使用async/await来创建向服务器发送请求的任务,然后将控制权交还给消息循环,直到它是时候发送另一个请求或响应。​​或者,如果您不想使用C#5,您应该创建一个任务完成源,并设置具有正确延续的任务。

简而言之:处理许多并行I / O任务的正确方法是创建非常少量的线程,每个线程一次只执行非常少量的工作。让I / O完成线程处理I / O的细节。 您无需雇佣800人来模拟发送800封信件。雇用两个人,一人观看邮箱,另一人写信。

答案 1 :(得分:2)

这种情况下的答案并非如此简单。这实际上取决于您希望如何模拟客户:

  1. 如果您想连接800个客户端,但不一定要同时连接,最好使用Task s。它们是轻量级的,可以有效地使用底层ThreadPool

  2. 如果你真的希望客户端完全并行,我担心没有办法真正避免线程。没有神奇的方法来获得800个轻量级的同时执行任务。 Task抽象是轻量级的,因为它使用线程池。这意味着许多任务都映射到少量实际线程。但是,当然,这意味着它们并不真正并行运行,而是计划尽可能地运行。 ThreadPool的最大线程数为250(AFAIK),因此如果您使用Task,则一次实际执行的客户数不会超过250个。解决方案是将max threads设置为800,但此时它与使用经典线程相同。

答案 2 :(得分:1)

我会使用任务库并让任务库为您处理所有线程。你不想要启动800个线程。让一个同时进行多个同步线程是一个坏主意,这是另一个堆栈溢出问题,其中讨论了这个问题:Maximum number of threads in a .NET app?

答案 3 :(得分:1)

对于这个application domains是最好的选择。

应用程序域是.NET应用程序执行的隔离的运行时单元。它提供了一个托管内存边界,一个用于应用程序配置设置的容器,以及为分布式应用程序提供通信接口。

每个.NET应用程序通常只托管一个应用程序域,当给定的进程/程序启动时,该域由CLR自动创建。在某个进程/程序中创建其他应用程序域有时非常有用(在像您这样的情况下)。使用多个应用程序域可以避免通信复杂化,并且可以使用多个单独的进程并隔离您的任务。

你想要什么,你有两个选择。

  1. 在同一域中的单独线程上启动X个线程。
  2. 这意味着您必须非常厌倦线程安全,这对于模拟多次登录,模拟客户端等任务来说将非常困难。

    1. 在每个应用程序域中的相同进程中启动X个线程。
    2. 这将使每个spun线程保持隔离状态,并且托管应用程序/程序也可以轻松访问。通过在X个独立的应用程序域中进行所有X仿真,每个域都将被隔离,并且无法通过静态类成员等干扰另一个客户端仿真。

      以下是Joseph Albahari的书C# 4.0 In a Nutshell的摘录,我强烈建议你这样做:

      40个并发客户端模拟的示例可能对您有用:

      class program
      {
          static void main()
          {
              // Create 40 domains and 40 threads.
              AppDomain[] domains = new AppDomain[40];
              Thread[] thread = new Thread[40];
      
              for (int i = 0; i < 40; i++)
              {
                  domains[i] = AppDomain.CreateDomain("Client Simulation " + i);
                  thread[i] = new Thread(SimulateClientInOtherDomain);
              }
      
              // Start all threads, passing to each thread its app domain.
              for (int j = 0; j < 40; j++)
                  threads[j].Start(domains[j]);
      
              // Wait for the threads to finish.
              for (int k = 0; k < 40; k++)
                  threads[k].Join();
      
              // Unload the application domains.
              for (int l = 0; l < 40; l++)
                  AppDomain.Unload(domains[l]);
          }
      
          // Thread start with input of with domain to run on/in.
          static void SimulateClientInOtherDomain(object domain)
          {
              ((AppDomain)domain).DoCallBack(Simulate);
          }
      
          static void Simulate()
          {
             Client simClient1 = new Client("Bill", "Gates", ...);
             simClient1.Simulate();
          }
      }
      

      我希望这会有所帮助。

相关问题