|
一般的GTK+程序都只有一个线程,就算有timer out还有idle也都是在main loop里面是同步执行的。
在执行widget的时候,对图形界面的画图操作,有两种形式,一种是阻塞的,比如gdk_draw_something或者cairo_draw_something。另外一种是非阻塞的,比如gtk_widget_queue_draw,gdk_window_invalidate_rect,这些非阻塞的操作会把expose事件放入事件队列,等到main loop空闲的时候再去真正的重画。如果在此后CPU一直忙的话,那么界面是不能得到刷新的。所以如果要做的更实时的话,应该采用的是阻塞的操作。但因为widget都是双缓冲的,所以即使用阻塞式的方法绘图的话,离屏的表面跟在屏的表面的交换还是要在loop idle的时候才会进行。也就是说GTK+的框架决定了,它的刷图不可能做到实时。所以一定要达到实时绘图的话,大家还是考虑OpenGL和OpenInventor吧。
GTK+的库里面,glib是线程安全的,gdk,gtk,pango,cairo这些库函数都是非线程安全的,所以在多线程的环境下,调用这些函数之前都是需要上锁的,为了达到支持多线程的目的,GTK+特地提供了一个全局的锁,可以使用gdk_threads_enter和gdk_threads_leave进行加锁和解锁。
如果在main loop的外面,开头调用了g_thread_init和gdk_threads_enter,结尾调用了gdk_threads_leave,那么这个loop里面的每个关于绘图的操作都是能自动加锁保护的,例外的是timeout还有idle回调,这些回调如果有绘图操作的话,那么是需要程序员手动加锁保护的。
所以如果有新的线程加入的话,它在对界面更新之前,应该加锁保护,完成之后,立刻释放锁。新线程创建的timeout和idle回调也应该加锁保护,因为这些回调是在main loop的线程里被调用的。
对win32的开发来讲,是有其特殊性的,很多情况下加锁和解锁不是必须的。有些gtk的函数在多线程环境下调用会产生死锁,如gtk_widget_show。这些东西还是要摸索,没有现成的文档可循。比如说main loop之外的线程在对toplevel的window的layout改变进行操作的时候,将会产生死锁。这类问题的解决方案是把那些引起死锁的操作放置到idle或者timeout的回调里去执行。 |
|