1. 系统设计要找到瓶颈,“合适才是最好的”,一个系统不可能各个方面都面面俱到,根据业务不同,情况不同,可以定制设计。而且有的设计方法不是采用了就一定会有提高,例如读写分离。
2. 紧耦合简单的业务往往都放在中心,例如告警与告警联动业务,设备管理与告警联动,另外一些特殊业务放在客户端,中心处理比较通用的业务
3. c++单元测试框架googletest 一个关于软件测试的博客,详细介绍了Googletest测试框架http://www.cnblogs.com/coderzh/ ,企图在vc6上使用gtest,但是gtest源码中用到了很多模板新特性和一些c++的行特性,例如static const 变量在类生命时候初始化,模板的偏特化等等,vc6都不支持。
4. 开发过程中需要别人配合,或者别人需要配合不是什么坏事情,从另个方面说明系统的复杂性与开发过程中角色的重要性。
5. 高手的一般有个特点是喜欢沟通,能够了解别人的想法,不光是与比自己水平高的人交流,与新手交流也能够了解系统设计复杂不容易理解需要改进的地方。
6. http中的二进制数据通过base64编码的原因是http为文本协议,里面一些字符有特殊含义,例如\r\n,如果二进制数据出现这些“字符”那么对解析会产生影响,bash64把二进制数据转化成a-z,A-Z,0-9共62个字符,这样编码后的数据就全是上面的字符,不会对协议解析产生影响。由于2的6次方为64,所以bash64编码六位对应一个字符,这样,原本3个字符,24位的数据,就会被编码成4个字符,所以编码后体积基本为原来的4/3。
7. 对于\r\n结尾的包尾,一个功能就是做校验,另外一个是可以在长度与接收长度不匹配时候找到下一包(做边界),要求不能再数据中出现\r\n
8. http://bbs.youkuaiyun.com/topics/380167545 关于tcp发送编码讨论,提出了一个新的观点,对于发送的数据是不定长的,或者大于4g的,可以把数据进行base64编码,不带包的长度,以\0为结束符号判断是否为完整的包。这样做从编解码考虑上有一个很明显的特征,不用考虑协议特性,这里传输的是数据,而且对接非常简单,可以很好的把传输层给剥离开来,再以后甚至添加新的协议定义,传输层也不用改变。而且编码的复杂度被大大的降低了,不要考虑粘包问题
9. 线程的存在是因为有需要cpu等待的东西,网络传输,I/O,逻辑处理,服务器响应等等。
10. 是否异步与多线程
1)他们并没有特别直接的关系,异步与同步相对,与线程个数无关
2)适用情况,与cpu使用率有关,如果一个过程cpu消耗很多,其他如I/O操作较少,那么使用单线程比较好。如果是I/O操作,那么可以使用异步,也可以使用多线程,再I/O操作不多的时候,开个几十个线程无所谓,但是当读写文件非常多的时候,那么需要开非常多的线程,这个时候对cpu消耗是非常大的,还有一点,异步编程一般是比多线程复杂的。
11. 封装接口时候,一定要从用户角度出发,用户会使用什么接口,会对什么地方困惑,一些什么对象管理器什么的,用户才不关心,也不会用,无用的接口使用会更多的错误。
12. 类的功能细化,不仅容易阅读维护,而且使用上也有优势,在一些需要加锁的情况下,细化类的功能可以大大减少锁的范围,例如简单的创建对象功能,可以用factory拿出来,这样对象创建不一定在对象的manager中,创建时候不必持有manager的锁
13. 关于返回对象内的指针问题,在不使用智能指针的情况下,要特别注意加锁处理,在get到指针前一定对对象指针所在管理者加锁,使用过程中不能释放,使用完毕后释放锁,并且特别考虑所有提前返回的路径,释放锁。
14. 对于网络线程,回调函数最好立刻转化成消息的形式发送出去,而不要给应用回调接口,否则一旦阻塞,整个服务器通信就挂掉了。在需要平凡加锁同步的地方,可以考虑使用消息的方式,例如实时数据的查询,实时数据的更新,扫到数据后发送消息过去给数据更新线程,数据更新线程可以一秒钟刷新下数据(使用高优先级)。还可以考虑通知的方式,既然设备变化通知不可靠,那么使用程序内的内存变化通知方式,维护两份数据,类似读写分离。
15. 与stl一样,跨dll使用需要特别注意。跨dll不能删除对象,对象在一个dll中new,在另外一个dll中删除可能导致问题。这个和编译器有关系,编译器可能给不同dll分配不同的堆空间,也可能在同一个堆空间,vc6在一个堆空间是没有问题的,如果不在同一个堆空间,那么删除就会错误。另外调用delete是先调用析构函数,在调用free方法,并不是在析构函数中快退出的时候调用free,如果是这样的话就不会错在这个问题,导致问题的原因也就是free方法调用的地方不一样。解决方法是提供虚的release方法。
16. 设备管理上,可以按照设备树来管理,也可以按照类别来管理,如果按照设备树来管理,感觉比较合理,但是这样,考上层的根设备接口就比较大,而且在拷贝对象的时候就非常麻烦了。按照类别管理,查找父子设备关系需要外部实现,但是拷贝就方便了。当然,可以定义拷贝部分的函数。
17. 可以对一个特别复杂的类对外提供一些简单的封装,也就是想暴露给用户的接口,有点想数据库中的视图view
18. 对于锁的使用,注意点第一当然是不要死锁,第二就是不要太长时间占用锁,如果占用锁的时间很长,阻塞了一些很重要的流程,那么就需要考虑增加新的细化的锁,对于得到的不能够拷贝构造的对象的指针,可以先用一个管理对象的大锁,lock住,然后得到对象指针后,调用对象的锁,自己lock住,当然一些不希望修改删除的地方也要加锁,例如析构函数。这个时候可以释放管理对象的锁,然后执行对象一些比较消耗时间的操作,操作执行完后释放对象锁。在拷贝出对象指针后可以考虑使用智能指针,但是使用了智能指针也有一个风险,那就是其他线程对于对象的修改,智能指针智能保证对象不被意外删除,但是修改管不了~~第三个就是锁竞争的开销了,锁竞争对于操作系统消耗如何评价呢?
19. 对于同步执行的问题,一方面可以使用锁的机制,另一方面简单可以使用队列机制使用单线程
20. 对于需求变化的地方,类一定要细化,层次要低,简单说也就是最后实现,最后考虑,留下可以扩展的空间
21. 类的设计,一部分是因为公用和职责细化而增加,一部分是因为需求变化而增加
22.线程一旦开始进入主函数开始执行,从线程外部就只有一种方式能对线程进行控制,就是用TerminateThread强行终止线程,除此之外没有任何手段可以对线程进行单方面控制。
如果想要在线程外对线程进行控制,必须在线程外和线程内同时下手,可以有3种办法:
1。使用全局变量进行条件判断,在线程中无限循环,在线程外改变循环条件达到控制的目的,但这种方法CPU使用率太高,几乎没有任何实用价值。
2。在线程中使用EVENT、TIMER、Semaphore等内核对象,用WaitForSingleObject、WaitForMultipleObjects、MsgWaitForMultipleObjects等API让线程阻塞等待,在线程外通过SetEvent、ResetEvent、ReleaseSemaphore等API改变内核对象的状态从而达到控制的目的。
3。在线程中使用消息循环,就好象用SDK写普通的WIN32程序一样,在线程与线程之间通过PostThreadMessage发送消息,想干什么就干什么,我现在用的多线程都是工作在这种模式下,灵活而且稳定。
23.执行一个while(1);这样的死循环,然后看该进程的CPU使用率是多少,如果将近100%则说明是单核的,如果将近50%就是双核,将近25%是4核。
http://bbs.youkuaiyun.com/topics/270008490
线程优先级是在整个系统范围内的。
http://blog.youkuaiyun.com/fhbystudy/article/details/12856301
24. 释放线程时候要注意避免死锁,在释放执行的上下文中不能使待删除的线程,否则会发生死锁。一般停止线程是在创建线程的上线文中。与对象类似,谁创建,谁释放。
25. 软件设计核心工作就是把复杂问题简单化,软件代码或者说模块也是分层的,最上层应该是利用各种部件、封装好的模块来实现业务逻辑,这一层的代码一般用户甚至是能够得懂的,第二层应该是封装好的各种部件,各个部件最好耦合度低,接口功能简单,下面一层应该是与高级语言无关的汇编等等。
26. 析构函数中不能有锁?
27. ace的任务队列中是否有空闲线程管理?ace的threadManager也是通过类似condition wait的方式来管理线程的,在线程没有获得资源进入sleep时候会有一个类似空闲的线程队列的。所以在ace上自己管理空闲线程,意义不大。
28. “一般而言,类的static成员,像普通数据成员一样,不能再类的定义体中初始化,这个规则的一个列外是,只要初始化是一个常量表达式,整型const static 数据成员就可以在类的定义体中进行初始化” C++ primer中p401,但是vc6.0并不支持这个特性,以至于在有此特性的代码在编译时候需要修改代码,gtest中很多static const在类中初始化的使用。
29. 使用“to”字段这种透传策略与订阅策略各有优点,订阅可以很容易实现分布式,而to字段当作路由使用,可以减少系统之间的订阅量,直接透过系统先发送到一个小的子系统,然后在小的(子网络)子系统中可以在订阅处理,这样可以减少中心的订阅量,并且能够减少对中心的依赖,扩展灵活(能够在to的协议体中传递不同的content)