2012-04-05 35 views
11

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

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

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

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

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

+0

[请参阅相关讨论](http://stackoverflow.com/questions/10687850/task-factory-startnew-or-parallel-foreach-for-many-long-running-tasks) – 2012-10-20 15:48:43

回答

20

你当然不应该创建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封信件。聘请两个人,一个看邮箱和一个写信。

+2

如果它们很长运行与服务器的交互? – Tudor 2012-04-05 19:38:38

1

我会使用任务库并让任务库为您处理所有线程。你不想旋转800个线程。在同一时间同时执行多个并发线程是一个糟糕的主意,下面是另一个堆栈溢出问题:Maximum number of threads in a .NET app?

2

在这种情况下的答案并不那么简单。这真的取决于你希望你的客户进行模拟:

  1. 如果你想拥有800级连接的客户端,但不一定在同一时间,它的使用Task个好主意。它们重量轻,可以有效利用潜在的ThreadPool

  2. 如果你真的希望客户端完全平行,恐怕没有办法真正避免线程。没有不可思议的方式来获得800个轻量级的同时执行任务。 Task抽象是轻量级的,因为它使用线程池。这意味着许多任务都映射到少量的实际线程。但是,当然,这意味着它们并不是真正并行运行,而是计划在可能的情况下运行。 ThreadPool的最大线程数为250(AFAIK),因此如果您使用Task s,实际上不会超过250个“客户端”。该解决方案将最大线程设置为800,但此时与使用经典线程相同。

1

对于此application domains是你最好的选择。

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

每个.NET应用程序通常只托管一个应用程序域,该应用程序域在给定进程/程序启动时由CLR自动创建。在某个情况下,您可以通过单个进程/程序创建其他应用程序域。使用多个应用程序域可避免通信复杂性,并可使用多个单独的进程并提供您的任务分离。

对于你想要的你有两个选择。

  1. 在同一个域中的单独线程上启动X个线程。

这将意味着你将有非常疲乏的是线程安全的,这将是非常困难的这样一个任务,模拟多次登录,模拟客户等

  1. 启动X线在同一个进程中,每个进程在它自己的应用程序域中

这将保持每个纺纱线程隔离,也容易访问托管应用程序/程序。通过具有各位X模拟在X单独的应用程序域,每个域将被分离,并无法通过静态类成员等另一客户端仿真干扰

通过提取从约瑟夫阿尔巴哈利的书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(); 
    } 
} 

我希望这有助于。

相关问题