Archive for the ‘code|编程’ Category

C语言趣味题目

Wednesday, October 8th, 2008

这些是我搜集到的觉得有点意思的C语言题目,都不复杂,却有些难度,有兴趣的人可以一试(有些可能也不能说难,但可能不容易想答案):

  •  a+++++b是合法的C表达式吗?为什么?
  • 如何用宏定义实现SWAP(a,b),交换a和b的值。
  • 写一个C程序,不使用分号,实现输出Hello,World这个字符串。
  • 想一个算法,判断一个链表是否成环?

Mysql的随机选择行

Friday, September 26th, 2008

下雨了,秋老虎似乎可以过去了。。

今天用到一个功能,需要从Mysql表中随机选择一些行出来 ,原以为会有点复杂,没想到看了Google,只要在select后面加上order by rand()就可以了, 再加上Limit就可以限制选择的行数了,真是没想到Sql这么强大。

Select * from tbl_aaa ORDER by rand() LIMIT 10

上面的句子就是随机选择10行出来了。哦,方便!

分布式哈希-DHT/KAD

Monday, June 2nd, 2008

这个还是很强的,DHT-Distributed Hash Tables:分布式hash表(我怎么感觉通常带Distributed的都比较牛)。DHT是一种算法,把通常意义的hash表分布到了一个网络上,可能的应用有很多,比如把Web Proxy的负载分布到多个Proxy服务器上,又比如把一些集中的运算分布到网络上去,近些年最热门的应用则是在P2P网络中,比如在bittorrent/emule中,都集成了某种形式的DHT,在utorrent这个客户端中,就叫做DHT,而在eMule中被称为KAD

这篇文章使用Python代码简单的介绍了DHT的基本原理和核心算法,通俗易懂,不过老实说,一些细节我还没有弄懂,比如第一次加入网络的节点是否只能依靠手动建立路由等。也许应该去参考一下eMule的源码(它是开源的),不过现在暂时没有时间(懒的借口)。这个项目则有对KAD更严谨的Python实现。

注:早期的DHT算法很多都是大学里的研究成果,因此在Google上有很多相关论文可以检索到。

《Java Concurrency in Practice》

Sunday, May 18th, 2008

去了书店,看了《Java并发编程实践》,确实是一本不错的书,翻译的也不错。在书店只看了前三章 ,应该是整本书的基础。介绍Java中同步/互斥与共享的基本概念,同步/互斥其实是和语言不是很密切相关的,别的语言如C/C++都会遇到这个问题,但共享我却只在Java中看到,这个是Java平台(虚拟机)的实现约定,也就是多线程间共享变量时,一个线程写变量,另一个线程何时得到刷新的问题。以前从来没有想到Java会有这样的约定,挺奇怪的,应该是为了多CPU(或多核)的优化,幸而看了这本书。

后面的章节都没看,内容还很丰富,不过我向来认为这些技术类的书,应该先粗读一遍,理解概念,细节在用到时再看要好一些。基本上我觉得可以推荐一下这本书了,不错。

有好几天没有更新blog了,但生活还要继续,希望所有的人平平安安。

Java Volatile

Monday, May 12th, 2008

理解Java越多,就越发觉Java和C++的差异,这次是volatile。

共享
在C/C++中,volatile用于指示编译器对于这类变量的访问不要做优化,常用于一些硬件相关的读写中,在Java中,volatile的意思与C相似,但除了硬件相关外,还有一个更常见的使用场合,是用于多线程,在Java中,如果一个线程写变量,另一个线程读变量,读变量的线程可能不能“即时”获得被修改后的值,这是Java的一个实现约定,也是和JVM的实现相关,所以为了保证这种多线程共享变量的可见性,需要使用volatile。

互斥
要注意到,Java中synchronized也可以达到和volatile相同的作用,即使用synchronized做互斥时自动也达到了共享的目的,但反过来volatile就不能代替synchronized来做互斥,比如说非原子类型的变量(long/double之类)即使加volatile做++操作,也不是线程安全的,这时是需要使用synchronized的。

这样看来,Java中volatile的使用场合应该不是很多(因为多线程情况下,需要共享又不要互斥的情况不多),常见的使用诸如线程的控制循环退出的变量,一些标志变量等等,这些地方不涉及互斥,所以可以用volatile来简化。

Java线程与InterruptedException

Monday, May 5th, 2008

这个是Java中的一种异常,表示“被中断”,由于它是一个“checked exception”,所以在可能产生这种异常的场合下,你必须处理它。我一般看到的代码都是将之catch之后,什么也不做,为了确认这样的处理是否合适,找了一些资料来看,IBM Developerworks上的这篇文章做了比较详细的解释。

Java的线程都有一个interrupt标志,线程在运行时,如果别的线程想中断这个线程的运行(通过调用Thread.interrupt),会出现几种情况:

  • 当线程处于可以打断的阻塞状态,比如Thread.sleep()和Object.wait(),这时就会触发InterruptedException这个异常了,调用者必须处理一下这个异常,通常就是清理工作喽,或者继续往上抛,由调用者去决定后续的动作。
  • 当线程处于其它状态,比如努力干活,或虽然阻塞但不能被中断(比如一些IO操作),这时线程的中断状态位会被置位,剩下的事情就依赖线程自己的处理了,也就是说线程需要自己抽空来看这个标志位,如果不看的话,别人就没法打断你的运行了。

更多的细节,据说在《Java Concurrency in Practice》书中有详细的讲解(Amazon上5星哦),有些想看看,记得在书店应该是有中文版的《Java并发编程实践》,周末去看一看。

Java的互斥

Wednesday, April 30th, 2008

Java里做互斥还是很有意思的,不象C/C++,互斥依赖于平台实现。Java中的互斥是语言天生就提供的,Java有一个synchronized关键字,用来代替一般意义的lock/unlock,这样的好处起码是保证了lock/unlock的成对使用,另外代码看起来也清楚一些。而且Java里也不需要创建和删除互斥量,每个对象(Object)都带有一个互斥量,一般用这个就够用了。synchronized有好几种用法:

synchronized方法

这个是最常见的,直接在方法前面加synchronized,效果相当于进入方法时会lock这个对象,退出时unlock,这种方法加锁简单可靠,比如

public synchronized void method1(){

//do something under lock
}

但这种方法显然可能造成互斥区域扩大的风险,因为函数里的所有语句都自动放在互斥区内部了,这有时会影响效率。

synchronized语句

为了减小互斥区,可以直接使用synchronized语句,象这样:

private Object mutex = new Object();
public void method2(){
synchronized(mutex)
{
//do something under lock
}
//do otherthing
}

这种方法使用上更接近于C/C++的用法了,只是用一个Object来做互斥量了。

静态synchronized方法

语法上,Java也是支持在static method上加synchronized的,不过这就相当于lock了整个Class的锁了(非static的synchronized锁是基于对象的),粒度更大,这种用法我还没用过,书上如是说的。

最后,不要和Java1.5新的semphore混淆,Java1.5所提供的Semphore相当于大部分操作系统提供的计数器信号量,一般不用于做互斥(因为不能嵌套)。

J2ME的log

Saturday, April 26th, 2008

J2ME里面,直到MIDP2.0都没有Log的功能,本来不是大的问题,可以自己做一个嘛,不过在J2ME里面却不那么简单了,因为不知道把Log往哪里放,最常见的两个Log输出选项在J2ME都不具备(或者说不完全具备):

  • 控制台(console):大部分J2ME设备没有控制台,system.out.print的东东都会消失掉,不过Sun的模拟器倒是可以的,但毕竟是模拟器,没法进行设备上的调试。
  • 文件:这个要支持JSR75的设备才行,遗憾的是,很多设备都不支持(主要是早期的)。

所以,基本的做法只能是记在内存里,需要时把这个字符串取出来显示,不过这真不是一个好的解决方法,很简单的问题,如果代码崩溃了,就看不到的了。比较理想的一个方式是蓝牙(直接在PC端看Log),不过比记文件条件更高啊,需要两端都有蓝牙,并且设备端支持JSR82(N73好像支持),但起码我的笔记本还没有蓝牙。

J2ME paint

Wednesday, April 16th, 2008

在用J2ME的低级UI时,所有的界面绘制要靠自己来实现,主要是在paint消息中来做,这里涉及几个接口:paint,repaint,serviceRepaints,callSerially,我搞了一段时间才把它们弄明白。

paint

paint相当于是个回调函数,被J2ME主线程(处理UI和事件)来调用,任何时候我们都不能自己直接调用paint,程序在paint里面按部就班的画出界面即可,paint会带一个Graphics参数,用这个graphics去作图就可以了。

现在的很多J2ME平台已经默认支持双缓冲,所以在paint执行时,实际上界面并没有画出,而是等到paint执行完成后,一次性刷新到屏幕上的,不过这是和平台相关的。

repaint

这个是通知系统,我们需要刷新界面了,但仅仅是个通知,repaint会立即返回(无阻塞),paint会在合适的时候被系统调用,所以paint和repaint不是一一对应的,有一个repaint并不意味着一定会触发一个paint,多个repaint可能会被合并到一个paint调用。

如果在paint里面调用repaint,那么就意味着这个paint结束后会被再次调用,这在有些需要连续不段的刷屏的程序中可能有用,不过显然不太合适,比如下面这种代码。

public void paint (Graphics g) {
repaint();
}

serviceRepaints

如果调用serviceRepaints,则会直接立刻重绘界面,阻塞式调用,直到paint完成。所以如果我们写出下面的paint接口:

public void paint (Graphics g) {
repaint();
serviceRepaints();
}

后果相当于递归调用,并很快造成内存耗尽(Out of memory)异常,因为serviceRepaints里面又会直接调用paint(),所以使用serviceRepaints一定要小心,可以在某些地方使用serviceRepaints,但绝不能在paint里面调用。

callSerially

这个接口可以将一个Runable的接口抛在系统队列尾部,处理完消息之后,执行这个Runable接口的run,这在有些使用paint绘制的程序中会用到,可以在paint完以后,通过callSerially触发run接口,表示界面已经画完,可以做别的事情了,又不会阻塞系统队列。

《Facts and Fallacies of Software Engineering》

Monday, April 7th, 2008

这是最近在看的一本软件工程方面的书:《Facts and Fallacies of Software Engineering》(国内的翻译好像是叫软件工程中的真相和谬误),Amazon上的评级是4.5星,2002年出版,不过我是最近才看到。

这本书介绍软件工程中常见的一些“事实”和“误解”(前者55条,后者10条),看了一下这些条目,应该算是经验总结,实践性很强,比较对我的胃口,有些条目给我印象深刻,列举几条如下:

  • 最好的程序员比最差的程序员好28倍(这个,这个。。。怎么统计出来的)。
  • 工作环境对产品开发质量和开发效率有巨大的影响(深有体会)。
  • 开发工具和技术上的改进通常可以带来5%~35%的效率和质量提升,但它们总是被宣称能达到指数级别的改善(呵呵,这个对头)。
  • 软件开发人员总是谈论大量的工具,但他们评估的很少,买的很少,实际发挥作用的更是少的可怜(比较有讽刺意味的一条)。
  • 大多数软件预估是在开发最初期,开发人员甚至还没有弄清需求,没能理解问题就已经作了预估,这样的预估通常是错误的(非常残酷的一个现实)。
  • ......

但也不是每一条都很有新意,有些条目相对就比较陈旧一些(虽然是对的):

  • 软件开发的最重要因素不是程序员使用的工具和技术,而是程序员本身的质量。
  • 效率更多来自于设计,而不是编码。
  • .....

总的来说,这本书值得一看。