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