博文

目前显示的是 八月, 2018的博文

回忆一部电影:花季雨季

花季雨季上映于1997年,它热映的时候我正在上初中,一片懵懂。就记住片子里说,16岁时花季,17岁是雨季。于是我就无比渴望我的16、17岁。同时也很好奇,18岁又是什么。

影片中有一个小小的情节,影响了我的一生。片子里有一个学生,很勤奋,成绩很好,但是并不遭班主任的喜欢。他在家里挂了一幅字:“吃得苦中苦,方为人上人”。班主任看到后表示很忧虑,很不赞同这样的想法。她说: "一个人的真正价值首先决定于他在什么程度上和什么意义上从自我中解放出来"。其实当时我并不是很懂她在说什么,但是经过我的老师的解释后我勉强明白,争做“人上人”的想法是错的。虽然我不是特别明白它为什么是错的。

于是,这么多年我一直在思考这个问题。时不时的就会回想起这个,不断的警惕自己,不要有争做“人上人”的想法。然后逐渐的我就发现我与我这代人的主流价值观渐行渐远。我无意于说谁对谁错。在美国至少我学会了"no judgement"。我坦然接受我的失败。我一再错过了买房的机会,创业成功的机会。别人花4年升到Senior Software Engineer而我却花了10年。我若更“上进”一些生活必定大有不同。但我不太后悔。我只是回过头来很感谢这部电影,在我青春期给我留下了不可抹去的思想塑造。当我回头看我这失败的10年的时候,我内心是平静的。不怕你嘲笑,做Windows开发一直是我的梦想,现在我实现了。若有来生,我也许不会选择当一名软件工程师。我希望我能完成博士学位,然后成为一名医生、社会工作者,或者哪怕是个政客。


Thinking on thread pool

大多数应用程序的线程池的实现都是纯用户态的。这其中有两个例外,微软的Windows new ThreadPool API和苹果的GCD.

Availability:

首次发布的系统时间Windows New Thread Pool APIVista2006年GCDOS X 10.62009年
(Windows还有一套老的ThreadPool API,是从XP开始提供的,那个是纯用户态的)

传统来说,线程池的实现非常简单,启动几个线程,然后配合一个线程安全的队列就可以了。大约只需要几百行代码,利用C++标准库现有的thead/mutex/condition_variable这些原语就很容易实现一个跨操作系统的线程池。

但是往往,我们会发现同一进程里会有多个线程池,它们分别是针对不同的业务逻辑需要。于是如何设置每个线程池的大小就成了一个经验性的问题。如果我们可以实现一个通用的线程池,使得进程可以把它所有不同的workload都混在这同一个线程池中执行,同时这个线程池可以自动调节活跃线程数量使其等于CPU物理核数,那么这就是最理想的。

如果线程池收到的每个任务,在执行的时候都不会有任何阻塞或者等待,也就是说每时每秒都在进行纯CPU计算。那么我们线程的创建/销毁的策略就非常简单:让线程数量静态的等于这台机器总的CPU核数。这样就是最优的。

但是实际上用户是有可能进行阻塞式的操作,如:
1.  锁竞争
2.  等待某个事件(如条件变量,Windows Event)
3.  阻塞式IO

理想情况下不应该有任何阻塞性操作。但是实际上你常常会调用一些第三方的库(如MySQL Client),而它们不支持异步IO或非阻塞式IO,把它代码彻底改写又不大可能。

另外,当所有的线程都处于blocking状态时,就会导致新来的task无法被处理。我们称之为饥饿(starvation)。最严重的情况下,整个线程池里所有的线程都陷入无限的等待中,类似于死锁。为了防止这样的事情发生,需要一些简单粗暴的编程规则,如:等待task完成的线程不能与执行该task的线程在同一个线程池里。

出于以下几种原因,我们会希望使用操作系统特殊的API,而不是C++标准库的那些thread/mutex/condition_variable来实现线程池。

1. 让活跃的线程数量可以根据IO负载来调节。如果一个线程陷入I…