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

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

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

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

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

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

你当然不应该创建800个线程。

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

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

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

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

在现实生活中,这将是一个荒谬的解决方案。 像人一样,线程非常昂贵 。 您应该最小化您创建的线程数。

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

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

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

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

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

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

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

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

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

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

我会使用任务库并让任务库为您处理所有线程。 你不想旋转800个线程。 一次有多个同步线程是一个坏主意,这是另一个堆栈溢出问题,谈到这一点: .NET应用程序中的最大线程数?

对于这个应用领域是你最好的选择。

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

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

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

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

这意味着您必须非常厌倦线程安全,这对于模拟多个登录,模拟客户端等任务来说将非常困难。

  1. 在其自己的应用程序域中的每个进程中启动X个线程。

这将使每个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(); } } 

我希望这有帮助。