gtasklet 和 Nonblock Socket
最近一直在考虑下一版CoralFTP如何写,其中有个重要的问题就是原先的版本中使用了大量的回调函数,开始的时候觉得很方便,时间久了,代码多了,就发现实际上回调函数和用goto写出的程序差不多,甚至更难以理解。前几天在pygtk的maillist上发现了gtasklet,一个通过python的Generator机制来解决过多回调问题的类。仔细研究之后感觉它的设计真的很不错,而且也更加明白了Generator这个东西是拿来做什么的。所谓Generator,其实只是一种函数,但这种函数有个特殊的地方,它可以在运行到一半的时候中断,中断后函数的状态被保存下来,当再次调用此函数时,从上一次中断的地方继续运行,各种变量也会保持其原来的值。gtasklet就是一个使用Generator机制来模拟非抢先式线程的类。使用gtasklet时,一个应用中可以有很多个任务(task),这些任务在同一时刻只有一个执行。当任务执行到一半需要停顿时(譬如等待一定的时间、等待IO、等待信号等等),则通过Generator的机制暂停下来,当等待的条件发生,再由GTK的事件机制将其激活。
虽然用gtasklet完全代替 thread不见得合适,但用它来代替过度使用的回调机制是非常好的。譬如,当连接到FTP服务器时,用户只按一下按键,但程序要执行好几步操作,譬如连 接到FTP服务器,发送用户名、密码,如果正确则进入到相应的目录并列目录,如果错误要进行重试,同时在连接的过程中还必须随时更新界面,如显示用掉的时 间、重试的次数,允许用户中断连接等等。如果希望少用thread,在这个过程中就需要使用大量的回调函数,程序的可读性会变差。但如果使用 gtasklet,则可以用一个函数来控制整个过程,而且这个函数可以在主线程中运行,既不用担心程序会因为IO而停顿,又不用担心线程同步的各种问题。
当然,要想做到这一步,还有一个很重要的事情就是nonblock io。通常我们用到的IO操作都是会阻塞的,也就是说如果读出或写入的操作没有完成,则相应的调用不会返回。然而对于GUI程序,如果使用阻塞的IO,则 会导致程序在进行IO操作时无法更新其界面。因此应当通过多线程或者是非阻塞IO的方法来解决。相比较而言,多线程是比较容易的一种方法,但引入多线程同 样也会引入各种线程同步的问题;另一个解决方法就是用非阻塞的IO,然后通过select、poll等系统调用或者是g_io_add_watch等方式 来监控IO,直到有数据可以读或者可以写的时候才调用相应的回调函数进行读写,使用非阻塞IO的缺点主要是编程比较困难一些。
本来想贴一小段程序上来,但貌似这段程序无法通过系统的检查…… 
其实回调、线程都是很好的机制,用gtasklet也不能完全替换掉它们,也没有这种必要。最重要的就是把上面几种机制结合起来,发挥其相应的威力,而又不至于让程序如一团乱码一般难读就好了。