简介
GLib提供了对线程、互斥锁、递归锁、读写锁、条件量、线程私有数据、线程一次性初始化、位锁及指针位锁的操作。
数据结构
struct GThread // 线程
union GMutex // 互斥锁
struct GRecMutex // 递归锁
struct GRWLock // 读写锁
struct GCond // 条件变量
struct GPrivate // 线程私有数据
struct GOnce // 线程一次性初始化
函数列表
gpointer (*GThreadFunc) ()
GThread * g_thread_new ()
GThread * g_thread_try_new ()
GThread * g_thread_ref ()
void g_thread_unref ()
gpointer g_thread_join ()
void g_thread_yield ()
void g_thread_exit ()
GThread * g_thread_self ()
void g_mutex_init ()
void g_mutex_clear ()
void g_mutex_lock ()
gboolean g_mutex_trylock ()
void g_mutex_unlock ()
GMutexLocker * g_mutex_locker_new ()
void g_mutex_locker_free ()
#define G_LOCK_DEFINE()
#define G_LOCK_DEFINE_STATIC()
#define G_LOCK_EXTERN()
#define G_LOCK()
#define G_TRYLOCK()
#define G_UNLOCK()
void g_rec_mutex_init ()
void g_rec_mutex_clear ()
void g_rec_mutex_lock ()
gboolean g_rec_mutex_trylock ()
void g_rec_mutex_unlock ()
void g_rw_lock_init ()
void g_rw_lock_clear ()
void g_rw_lock_writer_lock ()
gboolean g_rw_lock_writer_trylock ()
void g_rw_lock_writer_unlock ()
void g_rw_lock_reader_lock ()
gboolean g_rw_lock_reader_trylock ()
void g_rw_lock_reader_unlock ()
void g_cond_init ()
void g_cond_clear ()
void g_cond_wait ()
gboolean g_cond_timed_wait ()
gboolean g_cond_wait_until ()
void g_cond_signal ()
void g_cond_broadcast ()
#define G_PRIVATE_INIT()
gpointer g_private_get ()
void g_private_set ()
void g_private_replace ()
#define g_once()
gboolean g_once_init_enter ()
void g_once_init_leave ()
void g_bit_lock ()
gboolean g_bit_trylock ()
void g_bit_unlock ()
void g_pointer_bit_lock ()
gboolean g_pointer_bit_trylock ()
void g_pointer_bit_unlock ()
guint g_get_num_processors ()
函数功能分类
线程
GThread * g_thread_new ()
GThread * g_thread_try_new ()
GThread * g_thread_ref ()
void g_thread_unref ()
gpointer g_thread_join ()
void g_thread_yield ()
void g_thread_exit ()
GThread * g_thread_self ()
互斥锁
void g_mutex_init ()
void g_mutex_clear ()
void g_mutex_lock ()
gboolean g_mutex_trylock ()
void g_mutex_unlock ()
GMutexLocker * g_mutex_locker_new ()
void g_mutex_locker_free ()
#define G_LOCK_DEFINE()
#define G_LOCK_DEFINE_STATIC()
#define G_LOCK_EXTERN()
#define G_LOCK()
#define G_TRYLOCK()
#define G_UNLOCK()
递归锁
void g_rec_mutex_init ()
void g_rec_mutex_clear ()
void g_rec_mutex_lock ()
gboolean g_rec_mutex_trylock ()
void g_rec_mutex_unlock ()
读写锁
void g_rw_lock_init ()
void g_rw_lock_clear ()
void g_rw_lock_writer_lock ()
gboolean g_rw_lock_writer_trylock ()
void g_rw_lock_writer_unlock ()
void g_rw_lock_reader_lock ()
gboolean g_rw_lock_reader_trylock ()
void g_rw_lock_reader_unlock ()
条件变量
void g_cond_init ()
void g_cond_clear ()
void g_cond_wait ()
gboolean g_cond_timed_wait ()
gboolean g_cond_wait_until ()
void g_cond_signal ()
void g_cond_broadcast ()
线程私有数据
#define G_PRIVATE_INIT()
gpointer g_private_get ()
void g_private_set ()
void g_private_replace ()
一次性初始化
#define g_once()
gboolean g_once_init_enter ()
void g_once_init_leave ()
位锁
void g_bit_lock ()
gboolean g_bit_trylock ()
void g_bit_unlock ()
指针位锁
void g_pointer_bit_lock ()
gboolean g_pointer_bit_trylock ()
void g_pointer_bit_unlock ()
guint g_get_num_processors ()
函数功能说明及综合演示
多线程编程
与标准的线程相关函数,Glib提供的函数少了pthread_detach和pthread_attr_xxx函数,前者作用是将线程标记为分离状态,后者作用是设置线程属性。
GThread * g_thread_new ()
GThread * g_thread_try_new ()
GThread * g_thread_ref ()
void g_thread_unref ()
gpointer g_thread_join ()
void g_thread_yield ()
void g_thread_exit ()
GThread * g_thread_self ()
g_thread_try_new:尝试创建一个线程,当创建失败,会产生错误原因。
g_thread_ref/g_thread_unref:每一个Glib对象内部都有引用计数这个概念,当引用计数减到0时,对象会自动释放。
g_thread_join:线程连接函数。此函数一般有两个作用:1.等待线程结束;2.,线程结束时,对线程资源进行回收,否则线程会变成僵尸线程。
示例代码如下:
源码见glib_examples\glib_threads\glib_threads_thread
#include <glib.h>
gint global_count = 0;
static gpointer _thread_func(gpointer data)
{
gint i = 0;
gpointer retval = NULL;
for(i=0;i<10000;i++) {
global_count += i;
}
g_print("count:%d \n", global_count);
}
void test_thread_join(void)
{
GThread *th1,*th2,*th3;
th1 = g_thread_new("th1", _thread_func, NULL);
th2 = g_thread_new("th2", _thread_func, NULL);
th3 = g_thread_new("th3", _thread_func, NULL);
g_thread_join(th1);
g_thread_join(th2);
g_thread_join(th3);
}
gint main(gint argc, gchar **argv)
{
g_test_init(&argc, &argv, NULL);
g_test_add_func("/thread/join", test_thread_join);
return g_test_run();
}
运行结果:
[root@centos7_6 build]# ./glib_threads_thread
/thread/join: count:49995000
count:99990000
count:149985000
OK
[root@centos7_6 build]# ./glib_threads_thread
/thread/join: count:49995000
count:99990000
count:111230911
OK
[root@centos7_6 build]# ./glib_threads_thread
/thread/join: count:49995000
count:99990000
count:149985000
OK
运行多次,大部分次数都符合预期,只有少部分次数不符合预期。很明显,这里需要线程同步。
互斥锁
互斥锁相关的操作有以下几个函数:
void g_mutex_init ()
void g_mutex_clear ()
void g_mutex_lock ()
gboolean g_mutex_trylock ()
void g_mutex_unlock ()
GMutexLocker * g_mutex_locker_new ()
void g_mutex_locker_free ()
#define G_LOCK_DEFINE()
#define G_LOCK_DEFINE_STATIC()
#define G_LOCK_EXTERN()
#define G_LOCK()
#define G_TRYLOCK()
#define G_UNLOCK()
针对上一节的程序,我们使用互斥锁进行线程同步。
示例代码如下:
源码见glib_examples\glib_threads\glib_threads_mutex
#include <glib.h>
gint global_count = 0;
GMutex *global_mutex = NULL;
static gpointer _thread_func(gpointer data)
{
gint i = 0;
gpointer retval = NULL;
g_mutex_lock(global_mutex);
for(i=0;i<10000;i++) {
global_count += i;
}
g_print("count:%d \n", global_count);
g_mutex_unlock(global_mutex);
}
void test_thread_mutex(void)
{
GThread *th1,*th2,*th3;
global_mutex = g_new0(GMutex, 1);
g_mutex_init(global_mutex);
th1 = g_thread_new("th1", _thread_func, NULL);
th2 = g_thread_new("th2", _thread_func, NULL);
th3 = g_thread_new("th3", _thread_func, NULL);
g_thread_join(th1);
g_thread_join(th2);
g_thread_join(th3);
g_mutex_clear(global_mutex);
g_free(global_mutex);
}
gint main(gint argc, gchar **argv)
{
g_test_init(&argc, &argv, NULL);
g_test_add_func("/thread/mutex", test_thread_mutex);
return g_test_run();
}
运行结果:
[root@centos7_6 build]# ./glib_threads_mutex
/thread/mutex: count:49995000
count:99990000
count:149985000
OK
[root@centos7_6 build]# ./glib_threads_mutex
/thread/mutex: count:49995000
count: 149985000
count:99990000
OK
可以看到,尽管无法控制单个线程的最终执行结果,但还是可以保证每个线程执行的结果是49995000的整数倍。
互斥锁创建:G_LOCK_DEFINE和G_LOCK_DEFINE_STATIC
G_LOCK_DEFINE可以对GMutex变量或其他任意类型的变量初始化。
G_LOCK_DEFINE_STATIC可以创建一个静态对象的互斥锁。
示例一:创建GMutex变量互斥锁
示例代码如下:
源码见glib_examples\glib_threads\glib_threads_mutex_init_gmutex
#include <glib.h>
gint global_count = 0;
GMutex global_mutex;
G_LOCK_DEFINE(global_mutex);
static gpointer _thread_func(gpointer data)
{
gint i = 0;
gpointer retval = NULL;
G_LOCK(global_mutex);
for(i=0;i<10000;i++) {
global_count += i;
}
g_print("count:%d \n", global_count);
G_UNLOCK(global_mutex);
}
void test_thread_mutex(void)
{
GThread *th1,*th2,*th3;
th1 = g_thread_new("th1", _thread_func, NULL);
th2 = g_thread_new("th2", _thread_func, NULL);
th3 = g_thread_new("th3", _thread_func, NULL);
g_thread_join(th1);
g_thread_join(th2);
g_thread_join(th3);
}
gint main(gint argc, gchar **argv)
{
g_test_init(&argc, &argv, NULL);
g_test_add_func("/thread/mutex", test_thread_mutex);
return g_test_run();
}
运行结果:
[root@centos7_6 build]# ./glib_threads_mutex_init_gmutex
/thread/mutex: count:49995000
count:99990000
count:149985000
OK
示例二:创建char类型变量互斥锁
示例代码如下:
源码见glib_examples\glib_threads\glib_threads_mutex_init_char
#include <glib.h>
gint global_count = 0;
gchar global_mutex = 0;
G_LOCK_DEFINE(global_mutex);
static gpointer _thread_func(gpointer data)
{
gint i = 0;
gpointer retval = NULL;
G_LOCK(global_mutex);
for(i=0;i<10000;i++) {
global_count += i;
}
g_print("count:%d \n", global_count);
G_UNLOCK(global_mutex);
}
void test_thread_mutex(void)
{
GThread *th1,*th2,*th3;
th1 = g_thread_new("th1", _thread_func, NULL);
th2 = g_thread_new("th2", _thread_func, NULL);
th3 = g_thread_new("th3", _thread_func, NULL);
g_thread_join(th1);
g_thread_join(th2);
g_thread_join(th3);
}
gint main(gint argc, gchar **argv)
{
g_test_init(&argc, &argv, NULL);
g_test_add_func("/thread/mutex", test_thread_mutex);
return g_test_run();
}
运行结果:
[root@centos7_6 build]# ./glib_threads_mutex_init_char
/thread/mutex: count:49995000
count:99990000
count:149985000
OK
示例三:创建静态变量互斥锁
示例代码如下:
源码见glib_examples\glib_threads\glib_threads_mutex_init_static
#include <glib.h>
gint global_count = 0;
G_LOCK_DEFINE_STATIC(global_mutex);
static gpointer _thread_func(gpointer data)
{
gint i = 0;
gpointer retval = NULL;
G_LOCK(global_mutex);
for(i=0;i<10000;i++) {
global_count += i;
}
g_print("count:%d \n", global_count);
G_UNLOCK(global_mutex);
}
void test_thread_mutex(void)
{
GThread *th1,*th2,*th3;
th1 = g_thread_new("th1", _thread_func, NULL);
th2 = g_thread_new("th2", _thread_func, NULL);
th3 = g_thread_new("th3", _thread_func, NULL);
g_thread_join(th1);
g_thread_join(th2);
g_thread_join(th3);
}
gint main(gint argc, gchar **argv)
{
g_test_init(&argc, &argv, NULL);
g_test_add_func("/thread/mutex", test_thread_mutex);
return g_test_run();
}
运行结果:
[root@centos7_6 build]# ./glib_threads_mutex_init_static
/thread/mutex: count:49995000
count:99990000
count:149985000
OK
递归锁
递归锁的操作函数如下:
void g_rec_mutex_init ()
void g_rec_mutex_clear ()
void g_rec_mutex_lock ()
gboolean g_rec_mutex_trylock ()
void g_rec_mutex_unlock ()
递归锁和非递归锁的区别:
同一个线程可以多次获取同一个递归锁,不会产生死锁。而如果一个线程多次获取同一个非递归锁,则会产生死锁。
Linux下pthread_mutex_t默认是非递归锁,可以显示地设置锁的PTHREAD_MUTEX_RECURSIVE属性,将pthread_mutex_t变为递归锁。
递归锁比较复杂,更容易出问题,一般推荐使用非递归锁。
下面演示非递归锁和递归锁的用法对比,在同一线程,调用非递归锁和递归锁。
同一线程调用非递归锁
示例代码如下:
源码见glib_examples\glib_threads\glib_threads_mutex_non_rec_err
#include <glib.h>
#define GLIB_THREADS_FORCE_QUIT 1 // force quit
gint global_count = 0;
GMutex *global_mutex = NULL;
GTimer *timer = NULL;
static gpointer _thread_func(gpointer data)
{
gint i = 0;
gpointer retval = NULL;
g_mutex_lock(global_mutex);
for(i=0;i<10000;i++) {
global_count += i;
}
g_usleep(GPOINTER_TO_INT(data)*1000*1000);
g_print("count:%d, elapsed:%f \n", global_count, g_timer_elapsed(timer, NULL));
g_print("before lock a non-recursion mutex again \n");
g_mutex_lock(global_mutex);
g_print("do something... \n");
g_mutex_unlock(global_mutex);
g_print("unlock a non-recursion mutex \n");
g_mutex_unlock(global_mutex);
}
void glib_threads_mutex_non_rec_err(void)
{
GThread *th1,*th2,*th3;
timer = g_timer_new();
g_timer_start(timer);
global_mutex = g_new0(GMutex, 1);
g_mutex_init(global_mutex);
th1 = g_thread_new("th1", _thread_func, GINT_TO_POINTER(3));
th2 = g_thread_new("th2", _thread_func, GINT_TO_POINTER(2));
th3 = g_thread_new("th3", _thread_func, GINT_TO_POINTER(1));
#if GLIB_THREADS_FORCE_QUIT
g_usleep(5*1000*1000); // sleep 5s
g_print("ERROR! quit with exception! \n");
exit(-1);
#endif
g_thread_join(th1);
g_thread_join(th2);
g_thread_join(th3);
g_mutex_clear(global_mutex);
g_free(global_mutex);
g_timer_destroy(timer);
}
gint main(gint argc, gchar **argv)
{
g_test_init(&argc, &argv, NULL);
g_test_add_func("/thread/mutex", glib_threads_mutex_non_rec_err);
return g_test_run();
}
运行结果:
[root@centos7_6 build]# ./glib_threads_mutex_non_rec_err
/thread/mutex: count:49995000, elapsed:2.000509
before lock a non-recursion mutex again
ERROR! quit with exception!
由于非递归锁在重复加锁之后程序无法退出,会一直卡死,影响自动化运行,因此上述代码加了一个强制退出机制,休眠5秒将会强制退出整个程序。
同一线程调用递归锁
示例代码如下:
源码见glib_examples\glib_threads\glib_threads_rec_mutex
#include <glib.h>
gint global_count = 0;
GRecMutex *global_rec_mutex = NULL;
GTimer *timer = NULL;
static gpointer _thread_func(gpointer data)
{
gint i = 0;
gpointer retval = NULL;
g_rec_mutex_lock(global_rec_mutex);
g_print("---sleep %d lock \n", GPOINTER_TO_INT(data));
for(i=0;i<10000;i++) {
global_count += i;
}
g_usleep(GPOINTER_TO_INT(data)*1000*1000);
g_print("count:%d, elapsed:%f \n", global_count, g_timer_elapsed(timer, NULL));
g_print("----sleep %d unlock \n", GPOINTER_TO_INT(data));
g_print("before lock a recursion mutex \n");
g_rec_mutex_lock(global_rec_mutex);
g_rec_mutex_unlock(global_rec_mutex);
g_print("after unlock a recursion mutex \n");
g_rec_mutex_unlock(global_rec_mutex);
}
void test_thread_rec_mutex(void)
{
GThread *th1,*th2,*th3;
timer = g_timer_new();
g_timer_start(timer);
global_rec_mutex = g_new0(GRecMutex, 1);
g_rec_mutex_init(global_rec_mutex);
th1 = g_thread_new("th1", _thread_func, GINT_TO_POINTER(3));
th2 = g_thread_new("th2", _thread_func, GINT_TO_POINTER(2));
th3 = g_thread_new("th3", _thread_func, GINT_TO_POINTER(1));
g_thread_join(th1);
g_thread_join(th2);
g_thread_join(th3);
g_rec_mutex_clear(global_rec_mutex);
g_free(global_rec_mutex);
g_timer_destroy(timer);
}
gint main(gint argc, gchar **argv)
{
g_test_init(&argc, &argv, NULL);
g_test_add_func("/thread/rec_mutex", test_thread_rec_mutex);
return g_test_run();
}
运行结果:
[root@centos7_6 build]# ./glib_threads_rec_mutex
/thread/rec_mutex: ---sleep 3 lock
count:49995000, elapsed:3.002168
----sleep 3 unlock
before lock a recursion mutex
after unlock a recursion mutex
---sleep 1 lock
count:99990000, elapsed:4.004852
----sleep 1 unlock
before lock a recursion mutex
after unlock a recursion mutex
---sleep 2 lock
count:149985000, elapsed:6.005817
----sleep 2 unlock
before lock a recursion mutex
after unlock a recursion mutex
OK
读写锁
读写锁适用场景:多个线程都会对资源进行读取,只有少量线程对资源进行写入,且读取频繁,写入不频繁。
读写锁的操作函数见下:
void g_rw_lock_init ()
void g_rw_lock_clear ()
void g_rw_lock_writer_lock ()
gboolean g_rw_lock_writer_trylock ()
void g_rw_lock_writer_unlock ()
void g_rw_lock_reader_lock ()
gboolean g_rw_lock_reader_trylock ()
void g_rw_lock_reader_unlock ()
下面举例演示读写锁。该程序创建了4个线程,两个写线程,两个读线程,每个线程占用给定时间的锁,然后休眠指定时间,把CPU让给其他线程。
示例代码如下:
源码见glib_examples\glib_threads\glib_threads_rwlock
#include <glib.h>
gint global_count = 0;
GRWLock *global_rwlock = NULL;
GTimer *timer = NULL;
static gpointer _thread_writer_small_func(gpointer data)
{
gint i = 0;
for(i=0;i<5;i++) {
g_rw_lock_writer_lock(global_rwlock);
g_print("writer small lock \n");
global_count += i;
g_print("writer small global_count: %d elapsed:%f \n", global_count, g_timer_elapsed(timer, NULL));
g_usleep((GPOINTER_TO_INT(data)*100*1000));
g_print("writer small unlock \n");
g_rw_lock_writer_unlock(global_rwlock);
g_usleep((GPOINTER_TO_INT(data)*100*1000));
}
}
static gpointer _thread_writer_large_func(gpointer data)
{
gint i = 0;
for(i=0;i<5;i++) {
g_rw_lock_writer_lock(global_rwlock);
g_print("writer large lock \n");
global_count += i*100;
g_print("writer large global_count: %d elapsed:%f \n", global_count, g_timer_elapsed(timer, NULL));
g_usleep((GPOINTER_TO_INT(data)*100*1000));
g_print("writer large unlock \n");
g_rw_lock_writer_unlock(global_rwlock);
g_usleep((GPOINTER_TO_INT(data)*100*1000));
}
}
static gpointer _thread_reader1_func(gpointer data)
{
gint i = 0;
for(i=0;i<5;i++) {
g_rw_lock_reader_lock(global_rwlock);
g_print("reader1 lock \n");
g_print("reader1 global_count: %d elapsed:%f \n", global_count, g_timer_elapsed(timer, NULL));
g_usleep((GPOINTER_TO_INT(data)*100*1000));
g_print("reader1 unlock \n");
g_rw_lock_reader_unlock(global_rwlock);
g_usleep((GPOINTER_TO_INT(data)*100*1000));
}
}
static gpointer _thread_reader2_func(gpointer data)
{
gint i = 0;
for(i=0;i<5;i++) {
g_rw_lock_reader_lock(global_rwlock);
g_print("reader2 lock \n");
g_print("reader2 global_count: %d elapsed:%f \n", global_count, g_timer_elapsed(timer, NULL));
g_usleep((GPOINTER_TO_INT(data)*100*1000));
g_print("reader2 unlock \n");
g_rw_lock_reader_unlock(global_rwlock);
g_usleep((GPOINTER_TO_INT(data)*100*1000));
}
}
void test_thread_rwlock(void)
{
GThread *th1,*th2,*th3,*th4;
timer = g_timer_new();
g_timer_start(timer);
global_rwlock = g_new0(GRWLock, 1);
g_print("\n");
g_rw_lock_init(global_rwlock);
th1 = g_thread_new("th1", _thread_writer_small_func, GINT_TO_POINTER(1));
th2 = g_thread_new("th2", _thread_writer_large_func, GINT_TO_POINTER(2));
th3 = g_thread_new("th3", _thread_reader1_func, GINT_TO_POINTER(1));
th4 = g_thread_new("th4", _thread_reader2_func, GINT_TO_POINTER(2));
g_thread_join(th1);
g_thread_join(th2);
g_thread_join(th3);
g_thread_join(th4);
g_rw_lock_clear(global_rwlock);
g_free(global_rwlock);
g_timer_destroy(timer);
}
gint main(gint argc, gchar **argv)
{
g_test_init(&argc, &argv, NULL);
g_test_add_func("/thread/rwlock", test_thread_rwlock);
return g_test_run();
}
运行结果:
[root@centos7_6 build]# ./glib_threads_rwlock
/thread/rwlock:
writer small lock
writer small global_count: 0 elapsed:0.000306
writer small unlock
writer large lock
writer large global_count: 0 elapsed:0.101299
writer large unlock
writer small lock
writer small global_count: 1 elapsed:0.303936
writer small unlock
reader1 lock
reader1 global_count: 1 elapsed:0.406466
reader2 lock
reader2 global_count: 1 elapsed:0.406485
reader1 unlock
reader1 lock
reader1 global_count: 1 elapsed:0.609238
reader2 unlock
reader1 unlock
writer small lock
writer small global_count: 3 elapsed:0.711543
writer small unlock
writer large lock
writer large global_count: 103 elapsed:0.813941
writer large unlock
writer small lock
writer small global_count: 106 elapsed:1.016799
writer small unlock
reader2 lock
reader2 global_count: 106 elapsed:1.119176
reader1 lock
reader1 global_count: 106 elapsed:1.119212
reader1 unlock
reader1 lock
reader1 global_count: 106 elapsed:1.321583
reader2 unlock
reader1 unlock
writer large lock
writer large global_count: 306 elapsed:1.423905
writer large unlock
writer small lock
writer small global_count: 310 elapsed:1.626801
writer small unlock
reader1 lock
reader1 global_count: 310 elapsed:1.727450
reader2 lock
reader2 global_count: 310 elapsed:1.727507
reader1 unlock
reader2 unlock
writer large lock
writer large global_count: 610 elapsed:1.929952
writer large unlock
reader2 lock
reader2 global_count: 610 elapsed:2.130435
reader2 unlock
writer large lock
writer large global_count: 1010 elapsed:2.332422
writer large unlock
reader2 lock
reader2 global_count: 1010 elapsed:2.534962
reader2 unlock
OK
通过结果可以看出:
- 读锁与读锁之间不互斥
- 读锁与写锁之间互斥
- 写锁与写锁之间互斥
条件量
条件量的操作函数如下:
// 初始化
void g_cond_init ()
// 销毁
void g_cond_clear ()
// 等待条件量
void g_cond_wait ()
// 等待条件量,超时则返回
gboolean g_cond_timed_wait ()
// 等待条件量,等到该信号或超时均退出
gboolean g_cond_wait_until ()
// 唤醒一个等待的线程,如果有多个线程则按入队顺序唤醒第一个
void g_cond_signal ()
// 唤醒所有等待的线程
void g_cond_broadcast ()
互斥锁只有两种状态:锁定和非锁定。条件量允许A线程等待B线程发送信号。A线程因等待条件变量信号而挂起。B线程可以发送条件变量信号给A线程或其他多个线程。为了避免出现唤醒丢失问题,条件变量和互斥锁常一起使用。
使用规则
pthread_cond_init LinuxThreads没有实现条件变量的cond_attr属性
pthread_cond_destroy 当有线程在该条件变量上等待,则销毁失败,返回EBUSY
pthread_cond_signal如果没有线程阻塞在条件变量上,调用本函数不会起任何作用
pthread_cond_broadcast会唤醒所有阻塞到本条件变量上的线程
pthread_cond_wait前需要由本线程加锁,因为其本身包含解锁和加锁两个动作。
/*************pthread_cond_wait()的使用方法**********/
pthread_mutex_lock(&mutex); /*lock*/
pthread_cond_wait(&cond, &mutex);/*lock-->unlock-->wait() return-->lock*/
pthread_mutex_unlock(&mutex); /*unlock*/
/*****************************************************/
在调用pthread_cond_wait前先调用mutex_lock,线程获得锁,进入条件等待,当条件不成立的时候:
- cond_wait函数先解锁mutex,然后进入休眠等待信号激活;
- 当有信号激活时,线程获得互斥锁,然后执行相关操作,最后释放锁;
- 如果有其他线程阻塞在该条件变量上,则其他线程开始竞争刚才释放的锁,得到锁的线程开始执行。
下面演示两个消费者一个生产者情况下,条件变量的用法。
示例代码如下:
源码见glib_examples\glib_threads\glib_threads_cond
#include <glib.h>
GMutex *mutex;
GCond *cond;
gpointer consumer1(gpointer data)
{
g_mutex_lock(mutex);
g_print("consumer1 lock\n");
g_cond_wait(cond, mutex);
g_print("consumer1 get cond \n");
g_print("consumer1 unlock\n");
g_mutex_unlock(mutex);
}
gpointer consumer2(gpointer data)
{
g_mutex_lock(mutex);
g_print("consumer2 lock\n");
g_cond_wait(cond, mutex);
g_print("consumer2 get cond \n");
g_print("consumer2 unlock\n");
g_mutex_unlock(mutex);
}
gpointer producer(gpointer data)
{
g_mutex_lock(mutex);
g_print("producer lock\n");
g_print("producer unlock\n");
g_mutex_unlock(mutex);
g_cond_broadcast(cond); //通知所有等待条件变量的线程
// g_cond_signal(cond); //只通知一个等待条件变量的线程
}
void test_thread_cond(void)
{
GThread *thid1,*thid2,*thid3;
g_print("\n");
mutex = g_new0(GMutex, 1);
g_mutex_init(mutex);
cond = g_new0(GCond, 1);
g_cond_init(cond);
thid1 = g_thread_new("thid1", consumer1, NULL);
thid2 = g_thread_new("thid2", consumer2, NULL);
sleep(1);
thid3 = g_thread_new("thid3", producer, NULL);
g_thread_join(thid1);
g_thread_join(thid2);
g_thread_join(thid3);
g_cond_clear(cond);
g_mutex_clear(mutex);
g_free(mutex);
g_free(cond);
}
gint main(gint argc, gchar **argv)
{
g_test_init(&argc, &argv, NULL);
g_test_add_func("/thread/cond", test_thread_cond);
return g_test_run();
}
运行结果:
[root@centos7_6 build]# ./glib_threads_cond
/thread/cond:
consumer1 lock
consumer2 lock
producer lock
producer unlock
consumer1 get cond
consumer1 unlock
consumer2 get cond
consumer2 unlock
OK
[root@centos7_6 build]# ./glib_threads_cond
/thread/cond:
consumer2 lock
consumer1 lock
producer lock
producer unlock
consumer2 get cond
consumer2 unlock
consumer1 get cond
consumer1 unlock
OK
多次运行程序,会有以上两种结果,消费者1或消费者2先执行,得到锁,然后进入条件量等待状态,另一个消费者也得到了锁,进入条件量等待,先等待条件量的线程先得到条件量,然后解锁并退出线程。
以消费者1先得到锁为例,消费者1得到锁后,并没有解锁,但消费者2却得到了锁,消费者2不解锁的情况下,生产者得到了锁,并可以解锁,究其原因,是因为pthread_cond_wait函数本身有对锁先解锁后加锁的操作,消费者1得到锁后,发现条件量不满足,就会先解锁,等待其他线程执行。
g_cond_broadcast通知所有等待条件量的线程,g_cond_signal只通知一个等待条件量的线程。上述程序,如果将g_cond_broadcast改为g_cond_signal,则只会有一个线程执行结束,另一个线程由于得不到锁,会一直处于等待条件量状态。
线程私有数据
全局变量可以在所有线程被访问,但一个线程改变全局变量的值,其他线程访问该全局变量时,该值也会发生改变。
线程私有数据表面上看起来是一个全局变量,所有线程使用同样的变量名访问它,但它在每个线程又是单独存储的,一个线程改变线程私有数据的值,不会影响到其他线程。
线程私有数据可以存储任何类型的数据,但在所有线程存储的数据类型需要一致,不可以一个线程存储的是整型,另一个线程存储的是字符串类型。
线程私有数据的操作函数如下:
#define G_PRIVATE_INIT()
gpointer g_private_get ()
void g_private_set ()
void g_private_replace ()
下面举例演示。
示例代码如下:
源码见glib_examples\glib_threads\glib_threads_private
#include <glib.h>
GPrivate private = G_PRIVATE_INIT (g_free);
static gpointer _thread_1_func(gpointer data)
{
gpointer *pri_val = NULL;
pri_val = g_private_get(&private);
g_print("pri_val(ori): %s \n", (gchar *)pri_val);
g_private_set(&private, g_strdup("abcd"));
pri_val = g_private_get(&private);
g_print("pri_val(set): %s \n", (gchar *)pri_val);
}
static gpointer _thread_2_func(gpointer data)
{
gpointer *pri_val = NULL;
pri_val = g_private_get(&private);
g_print("pri_val(ori): %s \n", (gchar *)pri_val);
g_private_set(&private, g_strdup("123456"));
pri_val = g_private_get(&private);
g_print("pri_val(set): %s \n", (gchar *)pri_val);
g_private_replace(&private, g_strdup("AABBC"));
pri_val = g_private_get(&private);
g_print("pri_val(replace): %s \n", (gchar *)pri_val);
}
void test_thread_private(void)
{
GThread *th1,*th2;
g_print("\n");
th1 = g_thread_new("th1", _thread_1_func, NULL);
th2 = g_thread_new("th2", _thread_2_func, NULL);
g_thread_join(th1);
g_thread_join(th2);
}
gint main(gint argc, gchar **argv)
{
g_test_init(&argc, &argv, NULL);
g_test_add_func("/thread/private", test_thread_private);
return g_test_run();
}
运行结果:
[root@centos7_6 build]# ./glib_threads_private
/thread/private:
pri_val(ori): (null)
pri_val(set): abcd
pri_val(ori): (null)
pri_val(set): 123456
pri_val(replace): AABBC
OK
一次性初始化
多线程程序有时有这样的需求:不管创建了多少线程,有些初始化动作只能发生一次。举例来说,所有线程共享的互斥锁,必须只能初始化一次。如果由主线程来创建新线程,那只需要在
主线程初始化即可,但对于库函数,由于调用者在初次调用库函数前可能已经创建了这些线程,就无法这样处理,因此需要这样的库函数:无论首次为任何线程所调用,都会执行初始化动作。
可以通过g_once函数实现一次性初始化,其操作函数如下:
#define g_once()
gboolean g_once_init_enter ()
void g_once_init_leave ()
示例代码如下:
源码见glib_examples\glib_threads\glib_threads_once
#include <glib.h>
gint global_count = 0;
GRWLock *global_rwlock = NULL;
static gpointer _thread_once_func(gpointer data)
{
g_print("Will be called only once! \n");
return GINT_TO_POINTER(9999);
}
static gint _thread_inner_init_func(void)
{
static GOnce my_once = G_ONCE_INIT;
g_once(&my_once, _thread_once_func, NULL);
g_print("g_once retval: %d \n", GPOINTER_TO_INT(my_once.retval));
return my_once.status;
}
static gpointer _thread_inc_func(gpointer data)
{
gint i = 0;
gint status = 0;
status = _thread_inner_init_func();
g_print("%s: status %d \n", __FUNCTION__, status);
g_rw_lock_reader_lock(global_rwlock);
global_count ++;
g_rw_lock_reader_unlock(global_rwlock);
}
static gpointer _thread_dec_func(gpointer data)
{
gint i = 0;
gint status = 0;
status = _thread_inner_init_func();
g_print("%s: status %d \n", __FUNCTION__, status);
g_rw_lock_reader_lock(global_rwlock);
global_count --;
g_rw_lock_reader_unlock(global_rwlock);
}
void test_thread_once(void)
{
GThread *th1,*th2;
global_rwlock = g_new0(GRWLock, 1);
g_print("\n");
g_rw_lock_init(global_rwlock);
th1 = g_thread_new("th1", _thread_inc_func, NULL);
th2 = g_thread_new("th2", _thread_dec_func, NULL);
g_thread_join(th1);
g_thread_join(th2);
g_rw_lock_clear(global_rwlock);
g_free(global_rwlock);
}
gint main(gint argc, gchar **argv)
{
g_test_init(&argc, &argv, NULL);
g_test_add_func("/thread/once", test_thread_once);
return g_test_run();
}
运行结果:
[root@centos7_6 build]./glib_threads_once
/thread/once:
Will be called only once!
g_once retval: 9999
_thread_dec_func: status 2
g_once retval: 9999
_thread_inc_func: status 2
OK
一次性初始化时执行多个函数。
g_once只能执行一个函数,如果需要执行多个函数,可以使用下面的函数。
如果把要执行的代码封装到一个函数,仍可以使用g_once。
g_once_init_enter
g_once_init_leave
示例代码如下:
源码见glib_examples\glib_threads\glib_threads_once_section
#include <glib.h>
gint global_count = 0;
GRWLock *global_rwlock = NULL;
static gpointer _thread_once_section1_init(gpointer data)
{
g_print("section1 init \n");
}
static gpointer _thread_once_section2_init(gpointer data)
{
g_print("section2 init \n");
}
static gsize _thread_inner_init_func(void)
{
static gsize initialization_value = 0;
gsize setup_value = 0;
if (g_once_init_enter (&initialization_value)) {
// initialization code here
_thread_once_section1_init(NULL);
_thread_once_section2_init(NULL);
g_once_init_leave (&initialization_value, 9999);
}
return initialization_value;
}
static gpointer _thread_inc_func(gpointer data)
{
gint i = 0;
gint status = 0;
status = _thread_inner_init_func();
g_print("%s: status %d \n", __FUNCTION__, status);
g_rw_lock_reader_lock(global_rwlock);
global_count ++;
g_rw_lock_reader_unlock(global_rwlock);
}
static gpointer _thread_dec_func(gpointer data)
{
gint i = 0;
gint status = 0;
status = _thread_inner_init_func();
g_print("%s: status %d \n", __FUNCTION__, status);
g_rw_lock_reader_lock(global_rwlock);
global_count --;
g_rw_lock_reader_unlock(global_rwlock);
}
void test_thread_once(void)
{
GThread *th1,*th2;
global_rwlock = g_new0(GRWLock, 1);
g_print("\n");
g_rw_lock_init(global_rwlock);
th1 = g_thread_new("th1", _thread_inc_func, NULL);
th2 = g_thread_new("th2", _thread_dec_func, NULL);
g_thread_join(th1);
g_thread_join(th2);
g_rw_lock_clear(global_rwlock);
g_free(global_rwlock);
}
gint main(gint argc, gchar **argv)
{
g_test_init(&argc, &argv, NULL);
g_test_add_func("/thread/once", test_thread_once);
return g_test_run();
}
运行结果:
[root@centos7_6 build]
[root@centos7_6 build]# ./glib_threads_once_section
/thread/once:
section1 init
section2 init
_thread_dec_func: status 9999
_thread_inc_func: status 9999
OK
[root@centos7_6 build]# ./glib_threads_once_section
/thread/once:
section1 init
section2 init
_thread_inc_func: status 9999
_thread_dec_func: status 9999
OK
可以看到,无论inc还是dec线程哪个先运行,g_once_init_enter/g_once_init_leave中间的代码段都只会被调用一次。