multitheading model是魔鬼

很多问题本来很简单,但是一旦引入多线程之后,复杂度就指数般的上升,而且,
mutex所带来的额外的同步开销,可能远远抵消掉了多线程所引来的并发量的提
升。1、一个基本原则:轻量级的任务不应使用new thread per request的方式执行。
e.g.基本tcp/ip服务,例如echo。

原因是显而易见的,创建/销毁线程的开销远大于了执行的开销。采用threads
pool的方式能对此有所缓解,但是threads pool的设计又远增加了系统的实现的复
杂度。

2、多线程环境增加了缓存的复杂度,降低了缓存的效率,还可额外增加缓存同步
的难度。

一个典型的示例就是pentium4的HTT漏洞。本来好好的单核CPU,偏要用HTT技术伪装
双CPU,带来的问题就是上下文切换时L1/L2 cache内的数据的安全性无法得到保
证。(逻辑上是2个CPU,但是物理上确是1个cache。)如果采用真正的双CPU,则两
个CPU的cache之间的同步的问题便显得额外的重要和困难。

同样的问题也发生在了多线程应用程序上。如何管理好全局/局部缓存,成了一个
核心问题。

就我目前所遇到的问题而言。本来是一个很简单的查询/更改的应用。采用了sun
rpc接口,采用了berkeley db数据库做后端。本来没有什么问题,因为 sun rpc的
大部分server side functions都是MT unsafe的。我可以在启动的时候在server端
直接打开一个数据库句柄,然后服务停止的时候关闭它。

但是solaris 8+的rpcgen增加了-A选项。使得server端也有了线程池。问题就变得
很为显著。最安全的做法是每次来一个新请求都重新打开数据库,但是这样性能会
大大降低。因为需要反复的文件映射,尽管操作系统会在虚拟内存上做优化,但是
还是要比以前慢很多。
次之的问题是,如果rpc请求在30秒之内得不到回应,原先的请求就会被重发。被
请求的函数将会被重复执行多次(至少2次)
而一般而言,没有良好的方式解决这个问题。
最好的方式是采用tcp来代替udp来传输(NFSv3,v4中的解决方案)。但是即便如
何,在极端情况下,tcp连接 也会被重置,请求也会被重新传输。

再或者就在server端手动维护一个缓存池,对请求的参数和函数名进行缓存。如果
下次再有相同的应用,就把上次的请求的答案返回给他。但是,这里隐含一个条件
就是,server端的那个函数,必须是无副作用的。(事实上基本大部分应用都绝对
不是).设想一个简单的例子,假如服务器端提供的是rand()这样的远程函数呢?再
或者,假如服务器端的数据库已经被清理过了,但是客户端还是可以获得老的数
据。

solaris 8+的rpc对udp可采transaction id(XID)的方式,然后对request
id,client adress,3 numbers建缓存池。

但是无论如何,服务器必须在限定的时间内返回请求结果。服务器采用的是RTOS
吗?它能达到这一点吗?显然不。一旦涉及了IO,涉及了mutex,那么结果将……
所以,对于server端的rpc代码,要
1、永远不要采用read/write这样的阻塞式IO,统统用aio代替。
2、永远不要直接accquire一个锁,要先用try_accquire的方式尝试下,或者,给
accquire加上timeout值。
3、(还有很多很多...)

如何建立一套可靠的remoting 方案?
如何建立一套能对本地ipc更有效的ipc方案? 例如用shared memory代替socket,
就可以基本不使用系统调用就完成IPC.(唯一的系统调用是线程必须被阻塞的时候)

此博客中的热门博文

少写代码,多读别人写的代码

在windows下使用llvm+clang

tensorflow distributed runtime初窥