Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Threads in WCF service

Tags:

wcf

threadpool

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?

like image 636
dragonfly Avatar asked May 13 '26 07:05

dragonfly


1 Answers

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.

like image 118
marc_s Avatar answered May 16 '26 03:05

marc_s