我正在编写一个客户端模拟程序,其中所有模拟客户端针对服务器运行一些预定义的例程 - 这是一个在 azure 中运行的具有四个实例的 Web 服务器。
所有模拟客户端在连接到服务器后运行相同的例程。
在任何时候,我都想使用我的程序模拟 300 到 800 个客户。
我的问题是:我应该创建 N 个客户端类实例并在 N 个不同的线程中运行它们吗?或者
我应该使用任务库来做这些事情吗?
我正在编写一个客户端模拟程序,其中所有模拟客户端针对服务器运行一些预定义的例程 - 这是一个在 azure 中运行的具有四个实例的 Web 服务器。
所有模拟客户端在连接到服务器后运行相同的例程。
在任何时候,我都想使用我的程序模拟 300 到 800 个客户。
我的问题是:我应该创建 N 个客户端类实例并在 N 个不同的线程中运行它们吗?或者
我应该使用任务库来做这些事情吗?
您当然不应该创建 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 封信。 雇两个人,一个看信箱,一个写信。
在这种情况下,答案并不是那么简单。这实际上取决于您希望如何模拟您的客户:
如果您想连接 800 个客户端,但不一定同时连接,那么使用Task
s 是个好主意。它们是轻量级的,并且可以有效地利用底层ThreadPool
.
如果您真的希望客户端完全并行,恐怕没有办法真正避免线程。没有什么神奇的方法可以同时执行 800 个轻量级任务。抽象是轻量级的Task
,正是因为它使用了线程池。这意味着许多任务被映射到少量的实际线程。但是,当然,这意味着它们并不是真正并行运行,而是尽可能安排运行。的最大线程数为 250(AFAIK),因此如果您使用s ThreadPool
,一次实际执行的“客户端”不会超过 250 个。Task
解决方案是将最大线程数设置为 800,但此时它与使用经典线程相同。
我会使用任务库,让任务库为您处理所有线程。您不想启动 800 个线程。一次同时运行这么多线程是个坏主意,这是另一个讨论这个问题的堆栈溢出问题:.NET 应用程序中的最大线程数?
对于这个应用程序域是你最好的选择。
应用程序域是 .NET 应用程序在其中执行的运行时隔离单元。它提供托管内存边界、应用程序配置设置容器以及为分布式应用程序提供通信接口。
每个 .NET 应用程序通常只承载一个应用程序域,该域由 CLR 在给定进程/程序启动时自动创建。在单个进程/程序中创建其他应用程序域有时很有用(在您的情况下)。使用多个应用程序域可以避免通信复杂性,并使用多个单独的进程出现,并提供任务隔离。
对于你想要的,你有两个选择。
这将意味着您将不得不非常厌倦线程安全,这对于模拟多个登录、模拟客户端等任务将非常困难。
这将使每个旋转线程保持隔离,并且托管应用程序/程序也易于访问。通过让你们所有人在 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();
}
}
我希望这有帮助。