我正在编写一个客户端模拟程序,其中所有模拟客户端都针对服务器运行一些预定义的例程 - 这是一个运行在azure中的Web服务器,有四个实例。
所有模拟客户端在连接到服务器后运行相同的例程。
我想在任何时候使用我的程序模拟300到800个客户。
我的问题是: 我应该创建N个客户端类实例并在N个不同的线程中运行它们吗? OR
我应该使用任务库来做这些事吗?
答案 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)
这种情况下的答案并非如此简单。这实际上取决于您希望如何模拟客户:
如果您想连接800个客户端,但不一定要同时连接,最好使用Task
s。它们是轻量级的,可以有效地使用底层ThreadPool
。
如果你真的希望客户端完全并行,我担心没有办法真正避免线程。没有神奇的方法来获得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自动创建。在某个进程/程序中创建其他应用程序域有时非常有用(在像您这样的情况下)。使用多个应用程序域可以避免通信复杂化,并且可以使用多个单独的进程并隔离您的任务。
你想要什么,你有两个选择。
这意味着您必须非常厌倦线程安全,这对于模拟多次登录,模拟客户端等任务来说将非常困难。
这将使每个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();
}
}
我希望这会有所帮助。