1 回顾
1.1 Gnome相关
首先回顾一下GLib,GObject,GIO,Gtk的不同,因为下面会涉及到这些概念里面的函数。
所有这些都是由Gnome项目开发的库,一般都用于Gnome环境相关的应用程序。
- Gtk:GUI界面库。
- GLib:是一个通用的、可移植的实用程序库,它提供了许多有用的数据类型、宏、类型转换、字符串实用程序、文件实用程序、主循环抽象等等。
- GMainLoop
- GMainContext
- GThread
- GAsyncQueue
- GObject:又名GLib对象系统,对象库(最初用于C编程语言,它不是面向对象的,但由于出现了与许多其他语言的绑定)。最初是GLib的一部分。Gtk中积极使用的对象和Gtk中的许多对象(如Gtk Window)继承了基本的GObject类型。其实就是一个基类型系统。
- GIO:是一个库,为通用I/O、网络、IPC、设置和其他高级应用程序功能提供了有用的类。
- GApplication
- GSettings
- GTask
1.2 ps命令
#查看运行的程序pid(进程号)
ps aux | grep application(应用程序的名字)
# 查看线程中的进程 -T表示开启线程查看
ps -T -p PID
# 杀死进程
kill -9 PID(进程号)
第一个标注的地方是CPU和内存占用率,后面的943100是物理内存使用量,单位是k,此时kafka大约占用943M内存
程序中我编写了onvif线程和主线程(Gtk),GLib命名的id并不会在ps查询命令显示,只运行该软件大概占用90M内存空间。SPID表示线程ID。
1.3 top命令
# -H表示开启线程查看, -p指定进程ID
top -H -p PID
第二个vpf-app应该就是onvif线程
onvif_seek_thread = g_thread_new("vpf.application.onvfi", vpf_application_onvif_run, app);
参考4:对于top命令显示信息的描述非常详细
2 GTK 多线程
2.1 信号与回调函数
GObject对象的信号在那个线程发出,回调函数就是那个线程执行。(跟回调函数在那个线程连接无关)。
Signals get executed in the context they are emitted from. In which context the object is created or where connect() is called from doesn’t matter.
2.2 如何区别函数运行在那个线程
GLib中,线程是一个抽象的概念,不能获取ID,可以通过两种方法判断该函数运行在那一个线程。GThread结构体内容并不公有。(该结构是不透明的——它的任何字段都不能直接访问。)
- 通过
g_thread_self ()
获取GThread结构体地址 - 通过
pthread_t id = pthread_self ()
函数
(vpf-app:86269)××××,86269是进程ID,Gtk报致命错误,可能是由于其它线程操作了UI界面。我的错误是因为,onvif线程中操作GListStore,因为GListStore与UI界面GtkColumnView绑定着。
2.3 如何将GTK与线程一起使用?
参考:How do I use GTK with threads?
GTK要求所有GTK API调用都来自创建GtkApplication的同一个线程,或者调用gtk_init()(主线程)。
如果想在GTK应用程序中利用多线程,通常最好将长时间运行的任务发送到工作线程,然后在工作线程使用g_idle_add或者GAsyncQueue反馈结果给UI线程(主线程)。GIO提供了有用的工具GTask。
3 GLib
3.1 GThread
这是一个结构体(不是GObject对象),表示一个正在运行的线程。该结构体由g_thread_new()或g_thread_try_new()返回。通过调用g_thread_self()可以获得表示当前线程的GThread结构体。
GThread被引用,请参阅g_thread_ref()和g_thread_unref()。由它表示的线程在运行时持有一个引用,而g_thread_join()消耗它给出的引用,因此通常没有必要显式管理GThread引用。
该结构是不透明的——它的任何字段都不能直接访问。
通过线程锁、条件,可以实现控制工作线程运行,如果工作线程不需要长时间运行,只有UI操作的时候运行一会,使用GTask更理想。
/* UI线程 */
//告诉界面,我要让工作线程执行任务了
...
g_cond_signal (&onvif_seek_cond);
/* 工作线程 */
g_mutex_lock (&onvif_seek_lock);
/**
* 要先上锁,是因为g_cond_wait函数要先解锁(如果未上错,会出错),再进入阻塞,等待cond
* 等到cond,然后上锁,执行代码
* 这个线程不能进行数据添加,因为添加数据会刷新UI
*/
g_cond_wait (&onvif_seek_cond, &onvif_seek_lock);
...
...
g_mutex_unlock (&onvif_seek_lock);
注意
如果同一个线程在没有释放锁的情况下尝试连续两次执行g_mutex_lock(),则会导致死锁。这是因为当线程在第一次调用g_mutex_lock()时获得了互斥锁,并且在第二次调用g_mutex_lock()之前未释放互斥锁。这样,在第二次调用g_mutex_lock()时,线程将一直等待互斥锁被释放,从而导致死锁。
因此,应该避免在同一线程中连续两次调用g_mutex_lock()函数而不释放锁。一种常见的做法是使用g_mutex_trylock()函数来尝试获取互斥锁,如果获取不到,则返回并立即执行其他操作,而不是一直等待锁被释放。另外,在释放锁之前,应该确保在互斥锁保护的临界区内执行的所有操作都已经完成。
3.2 GMainContext
通过把源事件添加到工作线程或者UI线程
3.3 GAsyncQueue
4 GIO
4.1 GTask
4.2 GSettings
参考1:Linux下查看某一进程所占用内存的方法
参考2:Linux上如何查看某个进程的线程
参考3:linux ps命令,查看某进程cpu和内存占用率情况
参考4:Linux命令之top命令查看服务器CPU与内存占用