是的,线程池线程利用率存在差异。
CLR 线程池将线程分为两种类型:worker 和 I/O(有关它们的更多信息,您可以在Simple description of worker and I/O threads in .NET和MSDN中找到)。一般来说,线程池为您提供每个核心 250 个工作线程和 1000 个 I/O 线程,因此您可以使用工作线程来处理 WCF 服务输入,并使用 I/O 线程等待异步发送/接收操作完成(支持在 Windows 操作系统级别通过重叠 I/O机制)。
记住以上内容,让我们通过使用ThreadPool.GetAvailableThreads()方法来看看这两种方法都使用了哪些线程:
int worker;
int ioCompletion;
ThreadPool.GetAvailableThreads(out worker, out ioCompletion);
Console.WriteLine("{0} worker and {1} I/O threads are available", worker, ioCompletion);
我将只显示客户端线程池利用率的结果,但服务器端也是如此。
APM 方法用于单向 WCF 操作。
对于 WCF 合同:
[ServiceContract]
public interface IService1
{
[OperationContract(IsOneWay = true, AsyncPattern = true)]
IAsyncResult BeginDoSomething(int value, AsyncCallback callback, object state);
void EndDoSomething(IAsyncResult result);
}
让我们使用下一个代码从客户端向服务器发送 100 个请求:
ChannelFactory<IService1> channelFactory = new ChannelFactory<IService1>();
var client = channelFactory.CreateChannel();
for (int i = 0; i < 100; i++)
{
int worker;
int ioCompletion;
ThreadPool.GetAvailableThreads(out worker, out ioCompletion);
Console.WriteLine("{0} worker and {1} I/O threads are available", worker, ioCompletion);
client.BeginDoSomething(i, asyncCallback, null);
}
输出是:
1023 worker and 1000 I/O threads are available
1023 worker and 998 I/O threads are available
1023 worker and 996 I/O threads are available
1023 worker and 996 I/O threads are available
1023 worker and 996 I/O threads are available
1023 worker and 998 I/O threads are available
1023 worker and 998 I/O threads are available
1023 worker and 999 I/O threads are available
1023 worker and 998 I/O threads are available
1023 worker and 998 I/O threads are available
1023 worker and 998 I/O threads are available
1023 worker and 999 I/O threads are available
1023 worker and 999 I/O threads are available
1023 worker and 999 I/O threads are available
1023 worker and 998 I/O threads are available
1023 worker and 998 I/O threads are available
1023 worker and 998 I/O threads are available
1023 worker and 999 I/O threads are available
如您所见,我的 x4 核心机器上的所有工作线程都可用,并且正在使用几个 I/O 线程。
作为 TPL 任务运行同步单向操作。
对于 WCF 合同:
[ServiceContract]
public interface IService2
{
[OperationContract(IsOneWay = true)]
void DoSomething(int value);
}
让我们使用下一个代码从客户端到服务器运行 100 个请求(只是想注意 TPL 使用 CLR ThreadPool underhood):
for (int i = 0; i < 100; i++)
{
int worker;
int ioCompletion;
ThreadPool.GetAvailableThreads(out worker, out ioCompletion);
Console.WriteLine("{0} worker and {1} I/O threads are available", worker, ioCompletion);
Task.Run(() => client.DoSomething(i));
}
输出是:
1023 worker and 1000 I/O threads are available
1022 worker and 1000 I/O threads are available
1021 worker and 1000 I/O threads are available
1020 worker and 1000 I/O threads are available
1019 worker and 1000 I/O threads are available
1019 worker and 1000 I/O threads are available
1019 worker and 1000 I/O threads are available
1019 worker and 1000 I/O threads are available
1019 worker and 1000 I/O threads are available
1019 worker and 1000 I/O threads are available
1019 worker and 1000 I/O threads are available
1019 worker and 1000 I/O threads are available
1019 worker and 1000 I/O threads are available
1019 worker and 1000 I/O threads are available
1019 worker and 1000 I/O threads are available
1019 worker and 1000 I/O threads are available
1019 worker and 1000 I/O threads are available
1019 worker and 1000 I/O threads are available
1019 worker and 1000 I/O threads are available
1019 worker and 1000 I/O threads are available
如您所见,现在正在使用工作线程,但没有使用 I/O 线程。
那么,推荐的方法是什么?
总而言之,您的解决方案应该:
- 利用线程池中的工作线程和 I/O 线程(尤其是对于高负载的应用程序)来防止瓶颈;
- 在 Task 中封装异步操作,因此您可以获得 TPL 的所有好处和新的 C# async/await 特性;
- 异步执行 OneWay 操作是绝对合理的(考虑到有时OneWay实际上不是 OneWay)。
因此,推荐的方法是WCF 的基于任务的异步模式,它满足上述所有要求。
WCF 的基于任务的异步模式。
对于合同:
[ServiceContract]
public interface IService3
{
[OperationContract(IsOneWay = true)]
Task DoSomethingAsync(int value);
}
让我们再次发送 100 个请求:
for (int i = 0; i < 100; i++)
{
int worker;
int ioCompletion;
ThreadPool.GetAvailableThreads(out worker, out ioCompletion);
Console.WriteLine("{0} worker and {1} I/O threads are available", worker, ioCompletion);
client.DoSomethingAsync(i);
}
输出:
1023 worker and 1000 I/O threads are available
1023 worker and 998 I/O threads are available
1023 worker and 999 I/O threads are available
1023 worker and 998 I/O threads are available
1023 worker and 999 I/O threads are available
1023 worker and 998 I/O threads are available
1023 worker and 998 I/O threads are available
1023 worker and 999 I/O threads are available
1023 worker and 999 I/O threads are available
1023 worker and 998 I/O threads are available
1023 worker and 999 I/O threads are available
1023 worker and 998 I/O threads are available
1023 worker and 999 I/O threads are available