有一段代码:
class WCFConsoleHostApp : IBank
{
private static int _instanceCounter;
public WCFConsoleHostApp ()
{
Interlocked.Increment(ref _instanceCounter);
Console.WriteLine(string.Format("{0:T} Instance nr " + _instanceCounter + " created", DateTime.Now));
}
private static int amount;
static void Main(string[] args)
{
ServiceHost host = new ServiceHost(typeof(WCFConsoleHostApp));
host.Open();
Console.WriteLine("Host is running...");
Console.ReadLine();
}
#region IBank Members
BankOperationResult IBank.Put(int amount)
{
Console.WriteLine(string.Format("{0:00} {1}", Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread) + " Putting...");
WCFConsoleHostApp.amount += amount;
Thread.Sleep(20000);
Console.WriteLine(string.Format("{0:00} {1}", Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread) + " Putting done");
return new BankOperationResult { CurrentAmount = WCFConsoleHostApp.amount, Success = true };
}
BankOperationResult IBank.Withdraw(int amount)
{
Console.WriteLine(string.Format("{0:00} {1}", Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread) + " Withdrawing...");
WCFConsoleHostApp.amount -= amount;
Thread.Sleep(20000);
Console.WriteLine(string.Format("{0:00} {1}", Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread) + " Withdrawing done");
return new BankOperationResult { CurrentAmount = WCFConsoleHostApp.amount, Success = true };
}
#endregion
}
我的测试客户端应用程序在50个线程中调用该服务(服务是PerCall)。我发现非常令人不安的是当我添加Thread.Sleep(20000)WCF 每秒创建一个服务实例时使用池中的不同线程。
当我删除Thread.Sleep(20000)时,会立即实例化50个实例,并且使用大约2-4个线程来实现它 - 实际上我认为这是正常的。
有人可以解释为什么Thread.Sleep会在创建实例时导致这些有趣的延迟吗?
答案 0 :(得分:10)
您将实际的服务实施(IBank界面的实施)和您的服务主机混合在一个同一个类中。
这绝对是 NOT 良好做法。
默认情况下,WCF将通过设计为每个请求实例化一个新的服务实现类的单独副本。这使得编写服务变得更加容易(不需要对多线程大惊小怪 - 每个请求都有自己的类)
但是:您不应该将其与ServiceHost混合使用,因为您实际上只需要一个服务主机实例来托管可以处理数百或数千个请求的服务类。
所以 - 创建一个类
class BankImplementation : IBank
{
private static int _instanceCounter;
BankOperationResult IBank.Put(int amount)
{
Console.WriteLine(string.Format("{0:00} {1}", Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread) + " Putting...");
//WCFConsoleHostApp.amount += amount;
Thread.Sleep(20000);
Console.WriteLine(string.Format("{0:00} {1}", Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread) + " Putting done");
return new BankOperationResult { CurrentAmount = WCFConsoleHostApp.amount, Success = true };
}
BankOperationResult IBank.Withdraw(int amount)
{
Console.WriteLine(string.Format("{0:00} {1}", Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread) + " Withdrawing...");
//WCFConsoleHostApp.amount -= amount;
Thread.Sleep(20000);
Console.WriteLine(string.Format("{0:00} {1}", Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread) + " Withdrawing done");
return new BankOperationResult { CurrentAmount = WCFConsoleHostApp.amount, Success = true };
}
}
用于您的服务代码,然后单独一个(甚至可能在一个单独的项目中)用于托管您的服务代码:
class WCFConsoleHostApp
{
public WCFConsoleHostApp ()
{
Interlocked.Increment(ref _instanceCounter);
Console.WriteLine(string.Format("{0:T} Instance nr " + _instanceCounter + " created", DateTime.Now));
}
static void Main(string[] args)
{
ServiceHost host = new ServiceHost(typeof(BankImplementation));
host.Open();
Console.WriteLine("Host is running...");
Console.ReadLine();
host.Close();
}
}
现在你得到WCFConsoleHostApp
的一个实例,这将在host.Open()
启动WCF运行时并通过实例化BankImplementation
类来处理请求根据需要提供实例。
更新:嗯,WCF服务也被“限制”,例如你可以调整有多少并发调用和实例。默认情况下,您可以获得10个并发会话和16个并发呼叫。如果您的服务已经处理了16个并发呼叫并且这些呼叫已经休眠了一段时间,则不会创建和处理其他服务实例。
有关服务限制的详细信息,请参阅此blog post by Kenny Wolf。您可以根据需要调整这些最大值。
答案 1 :(得分:2)
我不知道这是对的,但是......
可能是您遇到了ThreadPool行为而不是WCF行为。由于线程保持打开状态,ThreadPool的行为可能是它会启动额外的线程来处理排队的工作,因为它通常会尝试保持线程数量以节省资源。
因此,理论上,WCF将为每个请求排队一个工作项,但由于线程未释放20秒,因此它们不会得到服务(超过初始请求,即)。 ThreadPool在一秒钟后看到这个,创建一个新线程,并从现有队列中窃取一些工作。每秒重复一次。
答案 2 :(得分:0)
您正在暂停服务 - 或模拟长时间运行的作业。 wcf只是创建了更多的线程来处理其他想要服务的客户端。
答案 3 :(得分:0)
我对此并不是百分之百确定,但您可能会遇到WCF服务的限制问题。请查看Throttling的this MSDN article部分。我希望这会有所帮助。