Thread pooling for web connections

[Main Reference: http://msdn.microsoft.com/msdnmag/issues/04/12/NETMatters/]

那么这次要说的是在client app中使用HttpWebRequest向server发送服务请求。在.NET 1.x中,如果你并发的HttpWebRequest数量太多的话,你会发现.NET开始抛这样一个exception: There were not enough free threads in the ThreadPool object to complete the operation. 这是什么原因呢?

答案总是在源代码里找。如果你看一下Shared Source CLI里的实现(顺便说一句,很推荐大家去下载一封SSCLI,.NET不是开源的,但SSCLI中的很多实现都忠实地体现了.NET中很多设计的概念和方法),会发现,其实在1.x的.NET framework中,HttpWebRequest其实是Asynchronous的:

 

public override WebResponse GetResponse()

{
  ....
  IAsyncResult asyncResult = BeginGetResponse(null, null);
  ....
  return EndGetResponse(asyncResult);
}

 

而BeginGetResponse其实是往Thread pool里queue一个work item (用过thread pool的朋友应该很熟悉QueueUserWorkItem这个API)。而这为什么会引起异常呢?

死锁 (deadlock)。

我们假设Thread Pool里只有一个thread,当你第一次调用GetResponse的时候,用掉了Thread pool里唯一的那个thread,GetResponse调用BeginGetResponse queue了一个work item来发送请求,然后再调用EndGetResponse来等待结果返回。不过很不幸,BeginGetResponse永远被执行了,因为唯一的thread已经被GetResponse用掉,而它(EndGetResponse))正在等待BeginGetResponse的返回,deadlock。

那么在现实情况中,thread pool显然不可能只有一个thread。然而,这种情况还是会发生的,比如thread pool有10个thread,当你连续queue 10个work item进去的时候,一样发生死锁。

在1.x中,.NET的权宜之计就是抛异常。在BeginGetResponse的最后,就在queue user work item之前,程序会检查System.Net.Connection.IsThreadPoolLow,如果是true,就跑出InvalidOpeartionException异常。

但如果你看一下上面SSCLI里这部分的实现,你会发现GetResponse里根本没有用到BeginGetResponse/EndGetResponse (呵呵,其实当时我也是一开始就开始看.NET的source code,结果发现根本没有什么BeginGetResponse/EndGetResponse,于是怀疑原文作者是不是在乱扯,后来看到文章的后面才明白),因为在.NET 2.0中,GetResponse已经是真正的使用synchronous call了,于是死锁的问题也不存在了。

那么在1.x的.NET中我们怎么人为干预以防止死锁的发生呢。

最显然的答案自然是使用semaphore(关于semaphore的概念就不讲了吧,大学operating system课程的这个是必修内容)。

但更可怜的是,.NET 1.x居然连semaphore也没有...... (汗,我看到这里才意识到很久以前读Jeff Prosise的《Programming Microsoft .NET Core Reference》的时候居然没有发现Multithreading一章里确实没有提到semaphore一个字)。

那么只能自己wrap一下win32的API了:

 

public sealed class Semaphore : WaitHandle
{
    public Semaphore() : this(1, 1) {}

    public Semaphore(int initialCount, int maximumCount)
    {
        if (initialCount < 0 || initialCount > maximumCount)
            throw new ArgumentOutOfRangeException("initialCount");
        if (maximumCount < 1)
            throw new ArgumentOutOfRangeException("maximumCount");
        IntPtr h = CreateSemaphore(
            IntPtr.Zero, initialCount, maximumCount, null);
        if (h == WaitHandle.InvalidHandle || h == IntPtr.Zero)
            throw new Win32Exception();
        Handle = h;
    }

    public void ReleaseOne()
    {
        int previousCount;
        if (!ReleaseSemaphore(Handle, 1, out previousCount))
            throw new Win32Exception();
    }

    [DllImport("kernel32.dll", SetLastError=true)]
    private static extern IntPtr CreateSemaphore(
        IntPtr lpSemaphoreAttributes, int lInitialCount,
        int lMaximumCount, string lpName);

    [DllImport("kernel32.dll", SetLastError=true)]
    private static extern bool ReleaseSemaphore(
        IntPtr hSemaphore, int lReleaseCount, out int lpPreviousCount);
}

 

怎样用Semaphore来控制并发线程数的代码就不贴了吧,经典的producer/consumer问题,或者你参考一下原文里的code snippet也行。

===============================================================

我的一些comments:

web connection pooling对于任何一个大型的web app都几乎是必须的,那么在2.0中,.NET是如何控制同步并发线程数的呢?对于web connection,还有没有其他可以配置的变量?

答案是有的,有兴趣的朋友可以看一下:ServicePoint, ServicePointManager, 和 system.net configuration element. 也许我下次再关于这个内容写点介绍.

 

在一个要求high security-concern, high flexibility的web应用中,以下的几条是我一直牢记的:

1. Sacrifice performance for design simplicity.

2. Sacrifice functionality for performace.

3. If a functionality is really desired and a must-have, at least provide a fallback so that user can get their result in a not intuitive but fast fashion.

复杂的design容易出错,更容易有安全漏洞,当你自以为自己的security mechanism坚不可摧的时候,无数hacker已经开始蠢蠢欲动。什么是最好的security? 我看如果能只用到public key cryptography来加密所有敏感数据才是最安全的(ok, I am over-simplifying here, but I hope you get my point)。

关于Performance,every extra second your service takes, you lose 10% more users. 我情愿先有一个简陋但可用且快速的系统,然后出alpha, beta测试,而不是上来就设计一堆caching mechanism, xslt translation, throttling,blahblah, 看上去很美,其实慢到无法忍受。后来再要修改臃肿的系统,代价就很大。

 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值