2

有一段代码:

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 在创建实例时会导致那些有趣的延迟吗?

4

4 回答 4

10

您正在将您的实际服务实现(IBank 接口的实现)和您的服务主机混合在同一个类中。

这绝对不是好的做法。

默认情况下,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();
    }
}

现在您获得了一个.WCFConsoleHostApphost.Open()BankImplementation

更新:嗯,WCF 服务也是“节流”的,例如,您可以调整有多少并发调用和实例。默认情况下,您会获得 10 个并发会话和 16 个并发调用。如果您的服务已经在处理 16 个并发调用并且这些调用休眠了一段时间,则不会创建和处理额外的服务实例。

有关服务限制的详细信息,请参阅 Kenny Wolf 的这篇出色的博客文章。您可以根据需要调整这些最大值。

于 2009-12-03T06:10:32.620 回答
2

我不知道这是否正确,但是...

可能是您遇到了 ThreadPool 行为而不是 WCF 行为。由于线程保持打开状态,线程池的行为可能是随着时间的推移它会启动额外的线程来处理排队的工作,因为它通常会尝试保持线程计数以节省资源。

因此,理论上,WCF 将为每个请求排队一个工作项,但由于线程在 20 秒内没有释放,因此它们不会得到服务(即在初始请求之后)。ThreadPool 在一秒钟后看到这一点,创建一个新线程,并从现有队列中窃取一些工作。每秒重复一次。

于 2009-12-03T06:28:15.823 回答
0

您正在暂停服务 - 或模拟长时间运行的作业。wcf 只是创建更多线程来处理其他需要服务的客户端。

于 2009-12-02T22:55:10.150 回答
0

我对此不是 100% 确定,但您可能会遇到 WCF 服务的限制问题。查看这篇 MSDN 文章的节流部分。我希望这有帮助。

于 2009-12-03T03:11:31.767 回答