2013年1月14日星期一

key生成器的两种持久化方案

需求:
        key生成器功能是生成一个唯一的key,不能重复。序列化是将已经生成的key序列化到磁盘,以防机器宕机,key丢失导致生成重复的key;我们的需求key是顺序增长的。


方案一:
        先生成key后持久化;用户模块调用key生成器时,生成器先将key返回给用户,并将返回给用户的最大key存储在内存中,到一定量后(比如,已经返回给用户64个key)将key序列化到文件。文件中还存在一个标识位,正常打开、关闭文件时设置相应的标识位。key生成器启动读取序列化文件时,判断文件是否被正常关闭,是则正常读取文件中最大的key,否则读取文件中的key加上64(略过,保证key不重复),再继续去生成key。

方案二:
        先持久化后生成key ;用户模块调用key生成器时,先将key序列化到文件,再将key返回给用户。直接序列化一个区间到文件,如程序启动时,key从1开始,不直接序列化1,而将64序列化到文件,这样之后从2-63的调用就不需再去序列化文件了,减少了文件的写操作。此方案不需在文件中存储任何标识位,机器宕机之后,序列化文件中的key肯定是最大的,后续的key不会重复。


        以上两种方案,都是从别人的代码和交流中获得的,我比较喜欢第二种方案,简单,省去了不必要的错误恢复。有时候换一种思路或者说逆向思路可以让你的设计更为简单、容易理解。

2013年1月11日星期五

多线程条件变量和互斥锁的使用

        多线程同步,是多线程编程中肯定会遇到的问题。常见的有pthread_mutex_t、pthread_rwlock_t和pthread_cond_t,前面两个是锁,后面叫条件变量(条件成立时,通知通过阻塞线程的机制;其中条件的修改是需要锁来保护的)。
        条件变量需要和互斥锁以及一个条件结合使用,条件表示某一种情况的达成,比如队列中有元素,可以去取元素了,互斥锁用来使多线程对条件的修改原子化,条件变量用来做通知,向队列放元素的线程通知取元素线程你们可以来取元素了。
        这里有一个令人头疼的问题,就是pthread_cond_signal()和pthread_cond_broadcast()函数是在锁内调用还是在锁外调用,如下:
         1、
         pthread_mutex_lock(&lock);
         pthread_cond_signal(&cond);
         pthread_mutex_unlock(&lock);

         2、
         pthread_mutex_lock(&lock);
         pthread_mutex_unlock(&lock);
         pthread_cond_signal(&cond);

         这个问题,考虑是否浪费了CPU,考虑任务的调度机制,令人头疼;最后在starckoverflow看到一种解释,我觉得很好:不要考虑以上那些不确定的因素,考虑代码正确性就可以,修改条件和通知其它线程条件被修改应该是在一起的,所以放到锁内更好理解。

         另外一点需要注意的是,条件判断使用while循环。
         pthread_mutex_lock(&lock);
         while(cond == NULL) {
                  pthread_cond_wait(&cond, &lock);
         }
         pthread_mutex_unlock(&lock);

2013年1月10日星期四

堆栈实现先进先出队列

实现思路:
        使用两个大小相同的堆栈(命名为s1,s2),来实现堆栈。队列包含一下几个操作:创建、注销、满判断、空判断、入队、出队。

创建:
       创建管理结构体,结构体包含队列容量、使用量、包含两个栈指针的数组。使用另外的库函数来创建堆栈。两个栈的大小和队列的容量相同。

注销:
       顺序注销两个栈,以及管理结构体。

满判断:
空判断:
       根据管理结构体中存储的容量和使用量来判断。

入队:
        首先使用前面的函数判断队列是否满,满则出错。将数据压入栈s1,并增加使用量计数。

出队:
        首先判断队列是否为空,空则出错。判断s2栈是否为空,空则将s1栈内容出栈,入栈到s2,s1的最后一个元素直接返回给函数调用者,不入栈;s2栈非空,则直接从s2中返回一个元素给函数调用者。返回元素要较少使用量计数。


代码实现见github(https://github.com/zhangst/exercise/tree/master/interview/fifo),这种实现还有一个问题,空间浪费。创建一个容量为16的队列,需要两个容量为16的堆栈才能实现,总空间占用为32了。


2012年12月14日星期五

linux gcc alignment 字节对齐

为什么对齐:
     方便CPU更快速的读取内存数据,Intel CPU也可以识别不对齐的元素,但效率会慢;而RSIC ARM 不能正确识别未正确对齐的变量
     变量对齐并不是C语言定义的,全由编译器根据当前平台的特性确定,所以不能直接确定某个struct的对齐情况。

三个前提:
    struct可以在元素之间和结尾添加填充字节
    数组内元素之间是不能添加填充字节
    可以创建struct类型的数组,要保证数据第二个元素也对齐

结构体不指定__attribute__((aligned()))属性:
       内置类型以自己的长度作为对齐条件,如 short 2字节对齐,int 4字节对齐;这些类型作为struct内元素时,会影响struct的对齐。
       struct内元素按自己的自身长度对齐,struct以最严格的元素的方式对齐(根据CPU字长看)
               32位PC,字长4字节,结构体内有元素大于4字节时,struct也以4字节对齐
               64位PC,字长8字节,struct内元素有大于等于8字节的元素时,struct以8字节对齐

结构体指定 type attribute __attribute__ ((aligned))属性:
        属性是对编译器的建议,编译器会尽量达成
        __attribute__ ((aligned(4)));   建议编译器此结构体以4字节对齐
        __attribute__ ((aligned));        建议编译器使用对此平台maximum useful alignment,有点扯,我平台测试的和手册结果居然不一样
        aligned 指定的长度也会受linker的限制,有些linker有自己的最大对齐值


参考资料:
http://stackoverflow.com/questions/364483/determining-the-alignment-of-c-c-structures-in-relation-to-its-members
http://gcc.gnu.org/onlinedocs/gcc-4.7.2/gcc/Type-Attributes.html#Type-Attributes

2012年4月14日星期六

学车记

      考完科目一,上周办完上车手续,这周总算是上车了。接下来的是连续4周,八个半天的高强度练习,希望自己可以顺利通过考试,拿到驾照。
        我选的是周末直通车,比计时班略贵,上车之前,所有直通车的学院被教练队长叫到办公室,做了40分钟的思想教育,主旨是要和教练和平共处,换位思考,理解教练。教育完后,上车,本以为马上可以开始学习了,结果坐在车里又被教练教育了30分钟,汗。直通车不同于计时班,从开始到结尾,始终是那一个教练教你。人与人之间的初次相处,保持克制相互友好是比较容易的,但是长时间的睦邻友好就不容易了,容易产生矛盾,我觉得这是队长和师傅说教的原因。

        今天主要是主要学习了换挡、离合等的使用,缓慢匀速的前进、倒车,控制车和几个杆的距离,其中最为难是缓慢匀速的前进,必须很熟练的控制离合的力度,动作太大容易熄火,很不易控制。对于贴库、移库教练给了各种口诀,一会需要背诵。
       驾校真是繁忙,学驾照的人真是多,驾校早上、下午、晚上三个时段,每次班车都拉来满满的一车人。教练一天要工作11个小时,风吹日晒,甚是辛苦啊。