Threads in WCF service
there is a piece of code:
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
}
My test client application calls that service in 50 threads (service is PerCall). What I found very disturbing is when I added Thread.Sleep(20000) WCF creates one service instance per second using different thread from pool.
When I remove Thread.Sleep(20000) 50 instances are instanciated straight away, and about 2-4 threads are used to do it - which in fact I consider normal.
Could somebody explain why when Thread.Sleep causes those funny delays in creating instances?
You're mixing up your actual service implementation (the implementation of your IBank interface), and your service host in one and the same class.
This is definitely NOT good practice.
By default, WCF will by design instantiate a new separate copy of your service implementation class for each request coming in. This makes writing the service much easier (no need to fuss with multi-threading - each request gets its own class).
BUT: you shouldn't mix that with the ServiceHost, since you really only need one service host instance to host a service class that can handle hundreds or thousands of requests.
So - create one class
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 };
}
}
for your service code, and then a separate one (possibly even in a separate project all together) for hosting your service code:
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();
}
}
Now you get one instance of your WCFConsoleHostApp
, which will spin up the WCF runtime at host.Open()
and handle the requests by instantiating as many BankImplementation
class instances as needed.
UPDATE: Well, a WCF service is also "throttled", e.g. you can tweak how many concurrent calls and instances there are. By default, you get 10 concurrent session and 16 concurrent calls. If your service is already handling 16 concurrent calls and those sleep for some time, no additional service instances will be creating and handled.
See this excellent blog post by Kenny Wolf on details about service throttling. You can tweak those maximums as you see fit.
I don't know that this is correct, but...
It might be that you're running into ThreadPool behavior rather than WCF behavior. Since the threads are staying open, the behavior of the ThreadPool may be that it will spin up additional threads to handle queued up work over time, as it will normally try to keep the thread count down to conserve resources.
So, theoretically, WCF will then queue a work item for each of the requests, but since the threads are not released for twenty seconds, they don't get serviced (past the initial request, that is). The ThreadPool sees this after a second, creates a new thread, and steals some work from the existing queue. Repeat every second.
You're pausing your service - or simulating long running jobs. wcf simply creates more threads to handle other clients that wants to be serviced.
I'm not 100% sure about this, but you may be running into throttling issues with your WCF service. Take a look at the Throttling section of this MSDN article. I hope this helps.
精彩评论