end.
在connect事件中,用户名在进入critical section之前被读入一个临时变量。这样做是为了避免由阻塞critical section带来的对客户端可能的减慢。这使得网络通信能够在进入critical section之前被执行。为了使得性能最佳,critical section中的代码应该越少越好。
timer1timer事件在主线程中被主窗体上的一个计时器触发。计时器的时间间隔可以被缩短来达到更新更加频繁的目的,但是可能会降低接受连接的速度。如果日志的功能被扩展到了服务器的其他地方,不仅仅是记录用户的连接,这更加剧了产生瓶颈的可能。更短促的时间的间隔,更新用户界面所需的时间就更少。然而许多服务器根本就没有用户界面,即便是有也一般是第二位的,比服务客户端的优先级低得多,从而这是一个很好接受的权衡。
tcritical section位于syncobjs unit中。syncobjs unit没有包括在delphi 4的标准版中。如果你正在使用delphi 4的标准版,在indy的网站上有一个syncobjs.pas,它虽然没有实现borland的syncobjs.pas中的所有内容,但是实现了tcriticalsection类。
tmultireadexclusivewritesynchronizer(tmrews)
在前面的例子中,tcriticalsection被用来保护对全局数据的访问。在那些情况中,全局数据只是一直被更新。然而,如果全局数据有时被只读的访问,使用tmultireadexclusivewritesynchronizer可能产生更加有效率的源代码。tmultireadexclusivewritesynchronizer是一个冗长而又难读的类。因此它将被简单的称作tmrews。
使用tmrews的优势是它允许多线程的并发读取,同时又与critical section一样允许读的时候只有一个线程访问。劣势是tmrews用起来要费更高的代价。
不再是enter/acquire和leave/release,tmrews有方法beginread,endread,beginwrite和endwrite。
关于tmrews的特别说明
在delphi 6之前,tmultireadexclusivewritesynchronizer在从一个read lock变为一个write lock时可能导致死锁。因此你绝对不应该使用把read lock变成write lock的特性,即便文档上说是可以这么做的。
如果你需要使用这个功能,有一个折衷的办法。那就是先释放read lock然后再获得write lock。然而一旦你获得了write lock,然后你必须再次检查首先迫使你要用一个write lock的条件。如果它仍然存在,执行需要做的事情,否则立刻释放write lock。
在使用delphi 6时,tmultireadexclusivewritesynchronizer还是有特别要考虑的地方。所有版本的tmultireadexclusivewritesynchronizer,包括update pack 1和update pack 2中的,都有可能导致死锁的严重问题。没有已知的解决办法。borland知道这个问题的存在,并且已经发布了非官方的补丁并且可能会发布官方的补丁。
kylix中的tmrews
kylix 1和kylix 2中的tmultireadexclusivewritesynchronizer内部是用critical section实现的,并且不会比使用critical section有任何优势。然而,它被包括进来是为了代码能够同时用于linux和windows。在kylix的未来版本中,tmultireadexclusivewritesynchronizer可能升级为如它在windows下的表现一样。
在critical section和tmrews之间选择
因为tmrews已经被问题缠上了,我的建议很简单,就是避免使用它。如果你决定使用它,你应当确信它确实是更好的选择而且你已经获得了一个不再产生死锁行为的打过补丁的版本。
在大部分情况下,对tcriticalsection的恰当应用可以产生几乎一样快的效果,而且在某些情况下是更快。学会在必要的地方优化你的tcriticalsection,因为对tcriticalsection的不恰当使用将对性能产生严重的负面影响。
任何资源保护问题的关键都是使用多个资源controller,并且让加锁区域尽可能的小。当能做到这些时,总是应当使用critical section,因为它是轻量的而且比tmrews更快。总的来说,除非你能够明显的判断tmrews的用处,总是使用critical section。
tmrews类在以下条件都满足时性能更好:
1、访问包括读和写
2、读是主要的
3、加锁的时间必须被扩展开来维护,不能被分解为更小的块。
4、tmrews类被合适地打上了补丁,并且已知工作正常了。
性能比较
如前面提到过地,critical section更加轻量因而速度更快。critical section是由操作系统实现的。操作系统是使用非常快速和精简的汇编代码来实现它们的。
tmrews类更加复杂因而会带来更多的额外负担。它必须管理请求者的列表来合理地管理双状态的加锁机制。
为了展示这些区别,创建了一个名为concurrencyspeed.dpr的示例项目。它执行了三个如下的简单的肚量:
1、tcriticalsection ╟ enter 和 leave
2、tmrews ╟ beginread 和 endread
3、tmrews ╟ beginwrite 和 endwrite
测试是在一个计数的循环中运行它们一定次数。为了测试的目的,缺省是100,000次。在我的测试中,产生了如下的结果(毫秒计):
tcriticalsection:20
tmrews(read lock):150