5_03_GLib库入门与实践_线程

简介

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中间的代码段都只会被调用一次。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值