一个基础知识
ThreadPool.SetMinThreads 用于根据需要在线程池中设置最小线程数。默认情况下,最小线程数设置为系统上的处理器数。当需要创建的线程数大于这个值时,clr会采取节流措施,每500ms产生一个新线程。
关于最小线程数设置的场景举例
一家饭店每小时有100顾客,每个顾客都消费1小时,消费时需要服务员全程服务
A方法:请100个服务员
B方法:请50个服务员,如果缺人招待的话,半小时会再来1个服务员
A方法情况下每小时都能让顾客不用等待;
B方法情况下每小时都增加了约50个未接待访客,且服务员每小时才增加2个,完全的入不敷出,这样25小时过后,就增加了50个服务员,达到100个服务员,这时终于出入相抵。但是这25小时累计下来的未接访客,可粗略认为约1000人(有2500/2之多)吧,所以服务员还得请,那最终就导致了这种方式下服务员请了一堆,超过A方法,但是有大量的长时等待顾客。
其实2种方式要接待的客人量是一样,只是你不如一开始就多派些人去做,不然到后面这样增,只是白白让前期效率低下而已。
同步与异步方式哪种更省性能的问题
假如,请客人入座点菜,和给客户上菜是2种服务员工作,且请的服务员都能干这事。
A方法:客户点菜后,服务员站在一边,等菜好了的通知再上菜
B方法:客户点菜后,服务员回去门口,等新来的顾客,或者等菜好了的通知上菜(不一定是针对自己服务的点菜)
问A方法与B方法哪种花费人工最少?
那么再加个假设,客户入座点菜是1小时,全程服务员陪同,上菜之后吃还要提供服务时间,又假定为1小时,所以100个服务员能接待100/h的客流量吗,不能,要再高一点,假如是200人吧
A方法情况下,每个服务员要至少工作2小时(1小时点菜+1小时陪吃+等上菜时间)才能回到门口,在第1小时的顾客点菜后,只有100人在门口接待,对第2小时来的顾客,剩余的100服务员可以应对,到第3小时,又来了100顾客,由于第2小时去招待的服务员还只进行约一半服务,第1小时的服务员还需要等各自的上菜时间那么久才能回来,所以会造成,门口的100客人要等一会才能陆续有空闲服务员来招待;
B方法情况下,第1 小时来的100客人,全部能接待,第2小时门口又有200服务员在接待,在第2小时内有100名服务员要陆续去端菜并服务1小时,但剩余的100完全空闲服务员足以应对第2小时来的顾客,第3小时门口又回来了第2小时招待入座的100服务员,于是又能招待来的100新顾客,第3小时内有100名服务员要陆续去端菜并服务1小时,除去服务入座的100服务员,剩余的100服务员也完全能handle,所以这种情况下顾客不用付出等待服务员的时间。
如果A方法也想要做到B方法不需要顾客等待服务员的效果,就必然要请更多的人。一个有员工在干等,一个是员工时时刻刻在工作,所以当然是B方法(异步)好,即接待同样多的顾客,仍是异步方法请的服务员总人数少。
异步方法给人一种错觉,就是我服务1个客户,本来只要1个员工的,现在变成了2个员工,那我不是增加了员工吗?其实不是,在同样多的活的情况下,如果员工在客人等菜时也等着,没有去干活,那么也需要另一个员工去干别的活。虽然对这个客户只用了1个员工,但是对另一个客户,另另一个客户又相应多了一个员工,换句话说,我2个员工可以接待3个客人,但用同步方法,3个客人就得用3个员工去接待。所以异步是省人工的。
生产中遇到的奇怪问题
那么为什么我用了异步方法,设置了足够多的初始人员,却还是有大量等待的客人,因而还是不断地在招工呢?
按异步方法来说,不管上菜有多慢,上菜需要1天,服务员也是够的,因为请客人入座点菜就行了,这个很快,服务员又可以回到门口。
还有一种情况是,同步阻塞调用异步方法,如厨房通知人去端菜(local queque),但是门口接客也需要人,出现虽然不断请人,但请的人都去门口接客了(global queue优先级更高),也会导致不断招工。这种情况下服务员点完菜都等在客人旁边 ,但是客人的菜没人去端,因此这个服务员也不能回到门口,继而请更多的服务员。
这就是一种死锁,也叫线程饿死。
当然这都是并发量上去,或者等菜时间变长会更容易导致一些。 小的客流量,小的上菜耗时,同步等待也并不会造成人员用尽,不至于堆积不至于到不停招工的情况。