自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

向高手學(xué)習(xí):Glib如何來(lái)封裝跨平臺(tái)的線(xiàn)程庫(kù)

系統(tǒng) Linux
這篇文章,按照下面這 2 張圖,來(lái)描述 glib 在 Linux 和 Windows 平臺(tái)上,是如何來(lái)進(jìn)行線(xiàn)程庫(kù)的設(shè)計(jì)的。
  • 一、前言
  • 二、glib 簡(jiǎn)介
  • 三、線(xiàn)程庫(kù)的設(shè)計(jì)
  • 四、總結(jié)

一、前言

這篇文章,按照下面這 2 張圖,來(lái)描述 glib 在 Linux 和 Windows 平臺(tái)上,是如何來(lái)進(jìn)行線(xiàn)程庫(kù)的設(shè)計(jì)的。

Linux 平臺(tái):

Windows 平臺(tái):

最近寫(xiě)了幾篇關(guān)于跨平臺(tái)的應(yīng)用程序設(shè)計(jì)思路,有些小伙伴在后臺(tái)留言詢(xún)問(wèn)關(guān)于一些通用的跨平臺(tái)庫(kù),看來(lái)這方面的需求還是很多的。

所謂的跨平臺(tái),無(wú)非就是希望用同一份應(yīng)用程序的代碼,可以編譯出在多個(gè)平臺(tái)上運(yùn)行的可執(zhí)行程序。

那么如何才能做到應(yīng)用程序代碼的平臺(tái)無(wú)關(guān)呢?很明顯需要中間的一個(gè)橋接層,把自己不想處理的、那些與平臺(tái)相關(guān)的、煩人的代碼丟給這個(gè)中間層去處理。

簡(jiǎn)單的說(shuō):就是那些需要處理與平臺(tái)相關(guān)的臟活、累活,都由這個(gè)中間層幫你做好了,我們?cè)趯?xiě)應(yīng)用程序時(shí),只需要關(guān)心自己的業(yè)務(wù)層事務(wù)就可以了。

如果沒(méi)有這個(gè)中間層的話(huà),你的代碼中可能會(huì)充斥著大量的#if...#else代碼。

而 glib 就是這樣的一個(gè)中間層跨平臺(tái)庫(kù),它提供了很多常用的封裝,線(xiàn)程庫(kù)只是其中的封裝之一,這篇文章我們主要來(lái)學(xué)習(xí)一下 glib 是如何來(lái)設(shè)計(jì)跨平臺(tái)的線(xiàn)程庫(kù)。

二、glib 簡(jiǎn)介

第一眼看上去的時(shí)候,很容易把 glib 與 glibc 混淆,它倆都是基于 GPL 的開(kāi)源軟件,但是卻屬于完全不同的概念。

glibc是GNU實(shí)現(xiàn)的一套標(biāo)準(zhǔn)C的函數(shù)庫(kù),而glib是gtk+的一套函數(shù)庫(kù)。

那么 gtk+ 是什么呢?使用 Linux 的小伙伴一定知道 gnome 這個(gè)桌面環(huán)境,gnome 就是基于 gtk+ 開(kāi)發(fā)的一套桌面系統(tǒng),而 glib 就是位于 gtk 后面的那位無(wú)名英雄。

glib可以在多個(gè)平臺(tái)下使用,比如Linux、Unix、Windows等。glib為許多標(biāo)準(zhǔn)的、常用的 C 語(yǔ)言結(jié)構(gòu)提供了相應(yīng)的替代物。

作為一名 C 語(yǔ)言開(kāi)發(fā)者,有時(shí)候我們會(huì)非常的羨慕 C++ 開(kāi)發(fā)者,標(biāo)準(zhǔn)庫(kù)(SDL)有辣么多的工具可用:鏈表、向量、字符串處理。。。

可是 C 語(yǔ)言里呢?哪哪都要自己去實(shí)現(xiàn)這些輪子。

不過(guò)反過(guò)來(lái)說(shuō),如果我們?cè)谌粘5拈_(kāi)發(fā)過(guò)程中,把自己編寫(xiě)的、從別處借鑒的那些好用的輪子都積累起來(lái),形成自己的“寶庫(kù)”,這也是一種經(jīng)驗(yàn)的體現(xiàn)、也是一種競(jìng)爭(zhēng)力。

如今,在 github 上也有很多雷鋒實(shí)現(xiàn)了高質(zhì)量的 C 庫(kù):有專(zhuān)注于跨平臺(tái)的、有專(zhuān)注于某個(gè)領(lǐng)域的(比如:網(wǎng)絡(luò)處理、格式化文本解析)。

glib 在解決跨平臺(tái)的同時(shí),也提供了其他很多有用的工具箱,例如:事件循環(huán)、線(xiàn)程池、同步隊(duì)列、內(nèi)存管理等等。

既然它提供的功能多,那么必然會(huì)導(dǎo)致體積比較大。這也是很多開(kāi)發(fā)者面對(duì)不同的選擇時(shí),放棄 glib 的原因。

不管如何,既然 glib 這么厲害,我們可以來(lái)學(xué)習(xí)它的設(shè)計(jì)思想,這可是比盲目的敲幾千行代碼更能提升一個(gè)人的元技能!

三、線(xiàn)程庫(kù)的設(shè)計(jì)

1. 線(xiàn)程相關(guān)的文件

在 Linux 系統(tǒng)中,創(chuàng)建線(xiàn)程一般都是通過(guò) POSIX 接口(可移植操作系統(tǒng)接口),例如:創(chuàng)建線(xiàn)程 API 函數(shù)是 pthread_create(...)。

在 Windows 系統(tǒng)中,創(chuàng)建線(xiàn)程有好幾種方式:

  • CreateThread()
  • _beginthread()

既然 glib 庫(kù)時(shí)專(zhuān)門(mén)用來(lái)解決跨平臺(tái)問(wèn)題的,那么它向上面對(duì)應(yīng)用層程序時(shí),一定是提供一個(gè)統(tǒng)一的接口;而向下面對(duì)不同的操作系統(tǒng)時(shí),調(diào)用不同系統(tǒng)中的線(xiàn)程函數(shù)。

glib 把這些線(xiàn)程相關(guān)的操作分別封裝在了平臺(tái)相關(guān)的代碼中,具體來(lái)說(shuō)如下圖:

  • Linux 系統(tǒng):gthread.c, gthread_posix.c 參與編譯,生成 glib 庫(kù);
  • Windows 系統(tǒng):gthread.c, gthread_win32.c 參與編譯,生成 glib 庫(kù);

關(guān)于這種跨平臺(tái)的文件構(gòu)建方式(也就是編譯啦),建議您看一下這篇小短文:跨平臺(tái)代碼的3種組織方式

2. 數(shù)據(jù)結(jié)構(gòu)

你一定聽(tīng)說(shuō)過(guò)這個(gè)公式:程序 = 數(shù)據(jù)結(jié)構(gòu) + 算法,對(duì)于一個(gè) C 語(yǔ)言項(xiàng)目,明白了數(shù)據(jù)結(jié)構(gòu)的設(shè)計(jì),對(duì)于理解整個(gè)程序的思路是非常重要的,在 glib 中也是如此。

glib 在設(shè)計(jì)線(xiàn)程庫(kù)的時(shí)候,分成 2 個(gè)層次:平臺(tái)無(wú)關(guān)部分,平臺(tái)相關(guān)部分。

平臺(tái)無(wú)關(guān)的數(shù)據(jù)結(jié)構(gòu)有(一些不影響理解的代碼就刪掉了):

  1. struct  _GThread 
  2.   GThreadFunc func; 
  3.   gpointer data; 
  4.   gboolean joinable; 
  5. }; 
  6.  
  7. typedef struct _GThread GThread; 
  8. struct  _GRealThread 
  9.   GThread thread; 
  10.  
  11.   gint ref_count; 
  12.   gchar *name
  13. }; 
  14.  
  15. typedef struct _GRealThread GRealThread; 

平臺(tái)相關(guān)的數(shù)據(jù)結(jié)構(gòu)有:

Linux 系統(tǒng):

  1. typedef struct 
  2.   GRealThread thread; 
  3.  
  4.   pthread_t system_thread; 
  5.   gboolean  joined; 
  6.   GMutex    lock; 
  7.  
  8.   void *(*proxy) (void *); 
  9.   const GThreadSchedulerSettings *scheduler_settings; 
  10. } GThreadPosix; 

Windows 系統(tǒng):

  1. typedef struct 
  2.   GRealThread thread; 
  3.  
  4.   GThreadFunc proxy; 
  5.   HANDLE      handle; 
  6. } GThreadWin32; 

仔細(xì)看一下每個(gè)結(jié)構(gòu)體的第一個(gè)成員變量,是不是發(fā)現(xiàn)點(diǎn)什么?

從層次關(guān)系上看,這幾個(gè)結(jié)構(gòu)體的關(guān)系為:

Linux 平臺(tái):

Windows 平臺(tái):

結(jié)構(gòu)體在內(nèi)存模型中意味著什么?占據(jù)一塊內(nèi)存空間。

而這幾個(gè)數(shù)據(jù)結(jié)構(gòu)都把"子"結(jié)構(gòu)體,放在"父"結(jié)構(gòu)體的第一個(gè)位置,就可以方便的進(jìn)行強(qiáng)制類(lèi)型轉(zhuǎn)換。

在以上內(nèi)存模型中,GRealThread 結(jié)構(gòu)體的第一部分是 GThread,那么就完全可以把 GRealThread 所處內(nèi)存的開(kāi)始部分,當(dāng)做一個(gè) GThread 結(jié)構(gòu)體變量來(lái)操作。

用 C++ 中面向?qū)ο蟮男g(shù)語(yǔ)來(lái)描述更準(zhǔn)確:基類(lèi)指針可以指向派生類(lèi)對(duì)象。

在下面的代碼中,可以看到這樣的操作。

3. 線(xiàn)程的創(chuàng)建

(1) 函數(shù)原型

平臺(tái)無(wú)關(guān)函數(shù)(gthread.c 中實(shí)現(xiàn))

  1. GThread *g_thread_new (const gchar *name
  2.               GThreadFunc  func, 
  3.               gpointer     data); 
  1. GThread * 
  2. g_thread_new_internal (const gchar *name
  3.                        GThreadFunc proxy, 
  4.                        GThreadFunc func, 
  5.                        gpointer data, 
  6.                        gsize stack_size, 
  7.                        const GThreadSchedulerSettings *scheduler_settings, 
  8.                        GError **error); 

平臺(tái)相關(guān)函數(shù)(gthread_posix.c or ghread_win32.c 中實(shí)現(xiàn))

  1. GRealThread * 
  2. g_system_thread_new (GThreadFunc proxy, 
  3.                      gulong stack_size, 
  4.                      const GThreadSchedulerSettings *scheduler_settings, 
  5.                      const char *name
  6.                      GThreadFunc func, 
  7.                      gpointer data, 
  8.                      GError **error); 

(2) Linux 平臺(tái)函數(shù)調(diào)用鏈

先來(lái)看一下 Linux 平臺(tái)上的函數(shù)調(diào)用關(guān)系:

如果你的手邊有源代碼,請(qǐng)關(guān)注 g_thread_new() 這個(gè)函數(shù)中的 func 和 data 這2個(gè)參數(shù)。

func 是最開(kāi)始用戶(hù)層傳入的線(xiàn)程執(zhí)行函數(shù),也就是用戶(hù)創(chuàng)建這個(gè)線(xiàn)程,最終想執(zhí)行的函數(shù)。data 是 func 函數(shù)所接收的函數(shù)參數(shù)。

如果直接面對(duì) Linux 操作系統(tǒng)編程,在調(diào)用 POSIX 接口函數(shù) pthread_create() 時(shí),一般是直接傳入用戶(hù)想要執(zhí)行的函數(shù)以及參數(shù)。

但是 glib 層并沒(méi)有直接把用戶(hù)層的函數(shù)直接交給 Linux 操作系統(tǒng),而是自己提供了 2 個(gè)線(xiàn)程代理函數(shù),在調(diào)用 pthread_create() 時(shí),根據(jù)不同的情況,把這2個(gè)代理函數(shù)之一傳遞給操作系統(tǒng):

第一個(gè)線(xiàn)程代理函數(shù):g_thread_proxy();

第二個(gè)線(xiàn)程代理函數(shù):linux_pthread_proxy();

至于傳遞哪一個(gè)代理函數(shù),取決于宏定義 HAVE_SYS_SCHED_GETATTR 是否有效。

下面是 g_system_thread_new() 函數(shù)簡(jiǎn)化后的代碼:

  1. g_system_thread_new (proxy, stack_size, scheduler_settings, 
  2.                                           name, func, data, error); 
  3.     GThreadPosix *thread; 
  4.     GRealThread *base_thread;   
  5.      
  6.     // 填充 base_thread 字段,重點(diǎn)關(guān)注下面2句 
  7.     base_thread->thread.func = func; 
  8.     base_thread->thread.data = data; 
  9.  
  10.     thread->scheduler_settings = scheduler_settings; 
  11.     thread->proxy = proxy;    
  12.      
  13.     #if defined(HAVE_SYS_SCHED_GETATTR) 
  14.       ret = pthread_create (&thread->system_thread, &attr, linux_pthread_proxy, thread); 
  15.     #else 
  16.       ret = pthread_create (&thread->system_thread, &attr, (void* (*)(void*))proxy, thread); 
  17.     #endif 

4. 線(xiàn)程的執(zhí)行

我們就假設(shè)這個(gè)宏定義 HAVE_SYS_SCHED_GETATTR 被定義了、是有效的,Linux 系統(tǒng)中的 pthread_create() 接收到 linux_pthread_proxy() 函數(shù)。

當(dāng)這個(gè)新建的線(xiàn)程被調(diào)度執(zhí)行時(shí),linux_pthread_proxy() 函數(shù)被調(diào)用執(zhí)行:

簡(jiǎn)化后的 linux_pthread_proxy() 函數(shù):

  1. static void *
  2. linux_pthread_proxy (void *data) 
  3.   // data 就是 g_system_thread_new 中 GThreadPosix 類(lèi)型指針,這是平臺(tái)相關(guān)的。 
  4.   GThreadPosix *thread = data; 
  5.   
  6.   if (thread->scheduler_settings) 
  7.    { 
  8.       // 設(shè)置線(xiàn)程屬性 
  9.       tid = (pid_t) syscall (SYS_gettid); 
  10.       res = syscall (SYS_sched_setattr, tid, thread->scheduler_settings->attr, flags); 
  11.    } 
  12.  
  13.   // 調(diào)用 glib 中的線(xiàn)程代理函數(shù),其實(shí)就是 g_thread_proxy()  
  14.   return thread->proxy (data); 

這個(gè)函數(shù)關(guān)注 3 點(diǎn):

  1. data 參數(shù): 就是 g_system_thread_new 函數(shù)中的GThreadPosix類(lèi)型指針,這是平臺(tái)相關(guān)的。
  2. 中間部分是設(shè)置線(xiàn)程屬性;
  3. 最后的 return 語(yǔ)句,調(diào)用了 glib 中第一個(gè)線(xiàn)程代理函數(shù) g_thread_proxy。

繼續(xù)貼一下這個(gè)函數(shù)的簡(jiǎn)化后代碼:

  1. gpointer 
  2. g_thread_proxy (gpointer data) 
  3.   // data 就是 g_system_thread_new 中 GThreadPosix 類(lèi)型指針,這是平臺(tái)相關(guān)的。 
  4.   // 這里把它強(qiáng)轉(zhuǎn)成平臺(tái)無(wú)關(guān)的 GRealThread 類(lèi)型。 
  5.   GRealThread* thread = data; 
  6.  
  7.   if (thread->name
  8.   { 
  9.       // 設(shè)置線(xiàn)程屬性:名稱(chēng) 
  10.       g_system_thread_set_name (thread->name); 
  11.   } 
  12.  
  13.   // 調(diào)用應(yīng)用層的線(xiàn)程入口函數(shù) 
  14.   thread->retval = thread->thread.func (thread->thread.data); 
  15.  
  16.   return NULL

這個(gè)函數(shù)也只要關(guān)注 3 點(diǎn):

  1. data 參數(shù): linux_pthread_proxy 函數(shù)傳過(guò)來(lái)的是 GThreadPosix 類(lèi)型指針,但是這里直接賦值給了 GRealThread 類(lèi)型的指針,因?yàn)樗鼈兊膬?nèi)存模型是包含的關(guān)系;
  2. 中間部分是設(shè)置線(xiàn)程名稱(chēng);
  3. 最后的 thread->thread.func (thread->thread.data) 語(yǔ)句,調(diào)用了用戶(hù)最開(kāi)始傳入的函數(shù)并傳遞用戶(hù)的 data 參數(shù)。

至此,用戶(hù)層定義的線(xiàn)程函數(shù) user_thread_func(data) 就得以執(zhí)行了。

那么,如果 glib 層沒(méi)有定義宏 HAVE_SYS_SCHED_GETATTR,那么 Linux 系統(tǒng)中 pthread_create() 接收到的就是 glib 中的第一個(gè)線(xiàn)程代理函數(shù) g_thread_proxy。

線(xiàn)程執(zhí)行的調(diào)用關(guān)系為:

5. Windows平臺(tái)函數(shù)調(diào)用鏈

先來(lái)看一下 Windows 平臺(tái)上創(chuàng)建線(xiàn)程時(shí)函數(shù)調(diào)用關(guān)系:

在 Windows 平臺(tái)上,glib 的線(xiàn)程代理函數(shù)是 g_thread_win32_proxy()。

當(dāng)這個(gè)新建的線(xiàn)程被調(diào)度執(zhí)行時(shí),函數(shù)調(diào)用關(guān)系是:

四、總結(jié)

實(shí)現(xiàn)這樣的線(xiàn)程函數(shù)代理設(shè)計(jì),關(guān)鍵是利用了 C 語(yǔ)言中的結(jié)構(gòu)體類(lèi)型中,把“父”結(jié)構(gòu)體類(lèi)型變量強(qiáng)制轉(zhuǎn)換成“子”結(jié)構(gòu)體類(lèi)型變量來(lái)使用,因?yàn)樗鼈z在內(nèi)存模型中,剛開(kāi)始部分的空間中,內(nèi)容是完全一樣的。

最后,我把文中的這些圖合并起來(lái),繪制成下面這 2 張圖,完整的體現(xiàn)了 glib 中的線(xiàn)程設(shè)計(jì)思路:

Linux 平臺(tái):

Windows 平臺(tái):

本文轉(zhuǎn)載自微信公眾號(hào)「IOT物聯(lián)網(wǎng)小鎮(zhèn)」,可以通過(guò)以下二維碼關(guān)注。轉(zhuǎn)載本文請(qǐng)聯(lián)系IOT物聯(lián)網(wǎng)小鎮(zhèn)公眾號(hào)。

 

責(zé)任編輯:武曉燕 來(lái)源: IOT物聯(lián)網(wǎng)小鎮(zhèn)
相關(guān)推薦

2010-05-24 19:31:48

SNMP服務(wù)

2010-05-24 19:28:59

SNMP服務(wù)

2020-03-05 10:36:12

云計(jì)算數(shù)據(jù)庫(kù)云平臺(tái)

2022-03-11 08:15:37

命令行工具指令

2014-03-12 10:00:26

移動(dòng)開(kāi)發(fā)跨平臺(tái)

2019-08-01 14:44:31

云存儲(chǔ)安全網(wǎng)絡(luò)

2021-12-24 07:56:10

Cmake動(dòng)態(tài)庫(kù)應(yīng)用程序

2011-09-05 11:27:17

Sencha Touc框架HTML5

2009-03-23 09:05:01

2011-06-21 14:01:07

QT 界面庫(kù) Embedded

2019-11-26 08:43:44

平臺(tái)桌面軟件

2010-05-19 13:27:58

IIS壓縮

2011-04-06 17:24:43

MySQL數(shù)據(jù)庫(kù)復(fù)位根用戶(hù)密碼

2010-04-27 17:14:36

AIX svmon

2010-05-18 13:33:45

IIS管理

2022-10-08 08:34:34

JVM加載機(jī)制代碼

2012-01-18 11:25:36

服務(wù)器優(yōu)化

2021-05-27 05:22:28

前端引擎平臺(tái)

2021-06-12 09:39:50

Python字典數(shù)據(jù)類(lèi)型Python基礎(chǔ)

2011-07-01 10:03:52

QT 數(shù)據(jù)庫(kù)
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)