GLIB:Threads

一组线程称为进程,一个进程内的所有线程通过共享内存来通信。优点是,共享变量/内存通信非常方便;缺点是,多线程程序对程序员编程能力要求很高,否则很容易发生莫名其妙的BUG。多线程就是为并行而设计的,因此程序员不能对线程之间的执行顺序做任何假定;多线程执行的顺序性必须通过在程序代码中的同步原语来直接保证。

GLib库设计多线程函数的目标之一是,提高多线程编程的可移植性。

1. “锁”原语

GMutex互斥锁。

  • void g_mutex_lock ( GMutex* mutex )
  • void g_mutex_unlock ( GMutex* mutex )

GRecMutex递归互斥锁。与GMutex不同的是,在一个线程中可以多次递归调用GRecMutex锁而不会导致该线程死锁。如果多次递归调用GRecMutex,尤其要注意GRecMutex锁的递归释放锁编程。

  • void g_rec_mutex_lock ( GRecMutex* rec_mutex )
  • void g_rec_mutex_unlock ( GRecMutex* rec_mutex )

GRWLock是读写锁。与GMutex不同的是,GRWLock区分线程对锁保护的读写行为,确保最多只有一个写能抢到锁;如果不存在写抢锁情况,多个读可以并发执行。

  • void g_rw_lock_reader_lock ( GRWLock* rw_lock )
  • void g_rw_lock_reader_unlock ( GRWLock* rw_lock )
  • void g_rw_lock_writer_lock ( GRWLock* rw_lock )
  • void g_rw_lock_writer_unlock ( GRWLock* rw_lock )

地址锁方法:

  • void g_bit_lock ( volatile gint* address, gint lock_bit ),设置地址锁;如果该地址锁已经被设置,那么该函数就阻塞,直到有g_bit_unlock解除该地址锁。
  • void g_bit_unlock ( volatile gint* address, gint lock_bit ),解除地址锁;如果该地址锁已经被设置,那么该函数就会去唤醒阻塞在该地址锁上的线程。

2. “条件同步”原语

条件GCond,线程获得某个条件的计算结果,如果该条件不成立,线程就阻塞在该条件上;一旦由于其他线程的执行修改了

  • void g_cond_wait ( GCond* cond, GMutex* mutex ),该函数判断条件是否成立,如果条件不成立就释放互斥锁并阻塞在该函数中,直到条件成立再抢锁,该函数返回时必须满足两个条件:条件成立且抢到锁。
  • gboolean g_cond_wait_until ( GCond* cond, GMutex* mutex, gint64 end_time ),与g_cond_wait不同之处在于,该函数的判断条件除了cond本身还有定时器超时条件end_time,当函数返回为真时表示判断条件cond发生了,参照g_cond_wait;如果函数返回为假,表示定时器超时发生了,线程并不持锁。
  • void g_cond_signal ( GCond* cond ),当线程调用该函数就会唤醒其他阻塞线程。

3. 线程私有数据原语

每个线程都有一个私有数据区,这个数据区很小,用GPrivate表示该数据区的key

  • gpointer g_private_get ( GPrivate* key ),类似读线程私有数据区,返回私有数据区数值;如果该私有数据区以前没有设置过,返回NULL。
  • void g_private_replace ( GPrivate* key, gpointer value ),类似写。如果私有数据区以前存在旧值,调用key上的GDestroyNotify处理函数处理旧的私有数据区。
  • void g_private_set ( GPrivate* key, gpointer value ),类似写,与g_private_replace不同,该函数不考虑旧值。

4. 一次性初始化机制

        一次性初始化机制(one-time initialization)主要用于在一个程序的生命周期内,某个数据结构或数据区仅仅被初始化一次,即从全零写入一个非零的初始化值;如果在初始化完成之前,并行执行的其他线程访问了该数据区就会被阻塞。

一个Gonce结构对应一个一次性初始化函数。

gboolean g_once_init_enter ( void* location )

void g_once_init_leave ( void* location, gsize result )

  static gsize initialization_value = 0;

  if (g_once_init_enter (&initialization_value)) {

      gsize setup_value = 42;

      g_once_init_leave (&initialization_value, setup_value);

  }

上述代码确保对initialization_value在程序的运行过程中仅仅初始化一次。

5. 线程

自从2.32版本之后,GLib线程子系统没有必要用g_thread_init()来初始化;在程序启动之初,线程子系统就自动初始化好了,线程创建函数和同步原语就可以直接调用了。

注意:无论程序代码中是否调用了线程函数g_thread_new(),程序员都不能假定程序在运行过程中不会产生新的线程。在某些情况下,GLib和GIO可能会自动生成线程,例如GDbus

GLib本身是线程安全的,即所有全局数据自动加锁访问。但是由于性能原因,某些数据结构并没有要求自动加锁,例如GHashTable。

GThread通常会把可能长时间阻塞的操作从主线程移到工作线程。如果有这个需要,考虑使用函数g_task_run_in_thread()。比起GThread,GThreadPool可能是个更好的选择,GThread支持线程重用和任务排队。

  • 8
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值