[導讀]一、前言二、glib簡介三、線程庫的設計四、總結(jié)一、前言這篇文章,按照下面這2張圖,來描述glib在Linux和Windows平臺上,是如何來進行線程庫的設計的。Linux平臺:Windows平臺:最近寫了幾篇關于跨平臺的應用程序設計思路,有些小伙伴在后臺留言詢問關于一些通用的跨...
- 一、前言
- 二、glib 簡介
- 三、線程庫的設計
- 四、總結(jié)
一、前言
這篇文章,按照下面這 2 張圖,來描述 glib 在 Linux 和 Windows 平臺上,是如何來進行線程庫的設計的。Linux 平臺:


#if...#else代碼。而 glib 就是這樣的一個中間層跨平臺庫,它提供了很多常用的封裝,線程庫只是其中的封裝之一,這篇文章我們主要來學習一下 glib 是如何來設計跨平臺的線程庫。二、glib 簡介
第一眼看上去的時候,很容易把 glib 與 glibc 混淆,它倆都是基于 GPL 的開源軟件,但是卻屬于完全不同的概念。glibc是GNU實現(xiàn)的一套標準C的函數(shù)庫,而glib是gtk 的一套函數(shù)庫。那么 gtk 是什么呢?使用 Linux 的小伙伴一定知道 gnome 這個桌面環(huán)境,gnome 就是基于 gtk 開發(fā)的一套桌面系統(tǒng),而 glib 就是位于 gtk 后面的那位無名英雄。
三、線程庫的設計
1. 線程相關的文件
在 Linux 系統(tǒng)中,創(chuàng)建線程一般都是通過 POSIX 接口(可移植操作系統(tǒng)接口),例如:創(chuàng)建線程 API 函數(shù)是 pthread_create(...)。在 Windows 系統(tǒng)中,創(chuàng)建線程有好幾種方式:既然 glib 庫時專門用來解決跨平臺問題的,那么它向上面對應用層程序時,一定是提供一個統(tǒng)一的接口;而向下面對不同的操作系統(tǒng)時,調(diào)用不同系統(tǒng)中的線程函數(shù)。glib 把這些線程相關的操作分別封裝在了平臺相關的代碼中,具體來說如下圖:
- CreateThread()
- _beginthread()

關于這種跨平臺的文件構建方式(也就是編譯啦),建議您看一下這篇小短文:跨平臺代碼的3種組織方式
- Linux 系統(tǒng):gthread.c, gthread_posix.c 參與編譯,生成 glib 庫;
- Windows 系統(tǒng):gthread.c, gthread_win32.c 參與編譯,生成 glib 庫;
2. 數(shù)據(jù)結(jié)構
你一定聽說過這個公式:程序 = 數(shù)據(jù)結(jié)構 算法,對于一個 C 語言項目,明白了數(shù)據(jù)結(jié)構的設計,對于理解整個程序的思路是非常重要的,在 glib 中也是如此。glib 在設計線程庫的時候,分成 2 個層次:平臺無關部分,平臺相關部分。平臺無關的數(shù)據(jù)結(jié)構有(一些不影響理解的代碼就刪掉了):struct _GThread
{
GThreadFunc func;
gpointer data;
gboolean joinable;
};
typedef struct _GThread GThread;
struct _GRealThread
{
GThread thread;
gint ref_count;
gchar *name;
};
typedef struct _GRealThread GRealThread;
平臺相關的數(shù)據(jù)結(jié)構有:Linux 系統(tǒng):typedef struct
{
GRealThread thread;
pthread_t system_thread;
gboolean joined;
GMutex lock;
void *(*proxy) (void *);
const GThreadSchedulerSettings *scheduler_settings;
} GThreadPosix;
Windows 系統(tǒng):typedef struct
{
GRealThread thread;
GThreadFunc proxy;
HANDLE handle;
} GThreadWin32;
仔細看一下每個結(jié)構體的第一個成員變量,是不是發(fā)現(xiàn)點什么?從層次關系上看,這幾個結(jié)構體的關系為:Linux 平臺:


3. 線程的創(chuàng)建
(1) 函數(shù)原型平臺無關函數(shù)(gthread.c 中實現(xiàn))GThread *g_thread_new (const gchar *name,
GThreadFunc func,
gpointer data);
GThread *
g_thread_new_internal (const gchar *name,
GThreadFunc proxy,
GThreadFunc func,
gpointer data,
gsize stack_size,
const GThreadSchedulerSettings *scheduler_settings,
GError **error);
平臺相關函數(shù)(gthread_posix.c or ghread_win32.c 中實現(xiàn))GRealThread *
g_system_thread_new (GThreadFunc proxy,
gulong stack_size,
const GThreadSchedulerSettings *scheduler_settings,
const char *name,
GThreadFunc func,
gpointer data,
GError **error);
(2) Linux 平臺函數(shù)調(diào)用鏈先來看一下 Linux 平臺上的函數(shù)調(diào)用關系:
如果你的手邊有源代碼,請關注 g_thread_new() 這個函數(shù)中的 func 和 data 這2個參數(shù)。func 是最開始用戶層傳入的線程執(zhí)行函數(shù),也就是用戶創(chuàng)建這個線程,最終想執(zhí)行的函數(shù)。data 是 func 函數(shù)所接收的函數(shù)參數(shù)。如果直接面對 Linux 操作系統(tǒng)編程,在調(diào)用 POSIX 接口函數(shù) pthread_create() 時,一般是直接傳入用戶想要執(zhí)行的函數(shù)以及參數(shù)。但是 glib 層并沒有直接把用戶層的函數(shù)直接交給 Linux 操作系統(tǒng),而是自己提供了 2 個線程代理函數(shù),在調(diào)用 pthread_create() 時,根據(jù)不同的情況,把這2個代理函數(shù)之一傳遞給操作系統(tǒng):第一個線程代理函數(shù):g_thread_proxy();第二個線程代理函數(shù):linux_pthread_proxy();至于傳遞哪一個代理函數(shù),取決于宏定義
HAVE_SYS_SCHED_GETATTR 是否有效。下面是 g_system_thread_new() 函數(shù)簡化后的代碼:g_system_thread_new (proxy, stack_size, scheduler_settings,
name, func, data, error);
GThreadPosix *thread;
GRealThread *base_thread;
// 填充 base_thread 字段,重點關注下面2句
base_thread->thread.func = func;
base_thread->thread.data = data;
thread->scheduler_settings = scheduler_settings;
thread->proxy = proxy;
#if defined(HAVE_SYS_SCHED_GETATTR)
ret = pthread_create ( 




