成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

向高手學習:Glib如何來封裝跨平臺的線程庫

系統 Linux
這篇文章,按照下面這 2 張圖,來描述 glib 在 Linux 和 Windows 平臺上,是如何來進行線程庫的設計的。
  • 一、前言
  • 二、glib 簡介
  • 三、線程庫的設計
  • 四、總結

一、前言

這篇文章,按照下面這 2 張圖,來描述 glib 在 Linux 和 Windows 平臺上,是如何來進行線程庫的設計的。

Linux 平臺:

Windows 平臺:

最近寫了幾篇關于跨平臺的應用程序設計思路,有些小伙伴在后臺留言詢問關于一些通用的跨平臺庫,看來這方面的需求還是很多的。

所謂的跨平臺,無非就是希望用同一份應用程序的代碼,可以編譯出在多個平臺上運行的可執行程序。

那么如何才能做到應用程序代碼的平臺無關呢?很明顯需要中間的一個橋接層,把自己不想處理的、那些與平臺相關的、煩人的代碼丟給這個中間層去處理。

簡單的說:就是那些需要處理與平臺相關的臟活、累活,都由這個中間層幫你做好了,我們在寫應用程序時,只需要關心自己的業務層事務就可以了。

如果沒有這個中間層的話,你的代碼中可能會充斥著大量的#if...#else代碼。

而 glib 就是這樣的一個中間層跨平臺庫,它提供了很多常用的封裝,線程庫只是其中的封裝之一,這篇文章我們主要來學習一下 glib 是如何來設計跨平臺的線程庫。

二、glib 簡介

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

glibc是GNU實現的一套標準C的函數庫,而glib是gtk+的一套函數庫。

那么 gtk+ 是什么呢?使用 Linux 的小伙伴一定知道 gnome 這個桌面環境,gnome 就是基于 gtk+ 開發的一套桌面系統,而 glib 就是位于 gtk 后面的那位無名英雄。

glib可以在多個平臺下使用,比如Linux、Unix、Windows等。glib為許多標準的、常用的 C 語言結構提供了相應的替代物。

作為一名 C 語言開發者,有時候我們會非常的羨慕 C++ 開發者,標準庫(SDL)有辣么多的工具可用:鏈表、向量、字符串處理。。。

可是 C 語言里呢?哪哪都要自己去實現這些輪子。

不過反過來說,如果我們在日常的開發過程中,把自己編寫的、從別處借鑒的那些好用的輪子都積累起來,形成自己的“寶庫”,這也是一種經驗的體現、也是一種競爭力。

如今,在 github 上也有很多雷鋒實現了高質量的 C 庫:有專注于跨平臺的、有專注于某個領域的(比如:網絡處理、格式化文本解析)。

glib 在解決跨平臺的同時,也提供了其他很多有用的工具箱,例如:事件循環、線程池、同步隊列、內存管理等等。

既然它提供的功能多,那么必然會導致體積比較大。這也是很多開發者面對不同的選擇時,放棄 glib 的原因。

不管如何,既然 glib 這么厲害,我們可以來學習它的設計思想,這可是比盲目的敲幾千行代碼更能提升一個人的元技能!

三、線程庫的設計

1. 線程相關的文件

在 Linux 系統中,創建線程一般都是通過 POSIX 接口(可移植操作系統接口),例如:創建線程 API 函數是 pthread_create(...)。

在 Windows 系統中,創建線程有好幾種方式:

  • CreateThread()
  • _beginthread()

既然 glib 庫時專門用來解決跨平臺問題的,那么它向上面對應用層程序時,一定是提供一個統一的接口;而向下面對不同的操作系統時,調用不同系統中的線程函數。

glib 把這些線程相關的操作分別封裝在了平臺相關的代碼中,具體來說如下圖:

  • Linux 系統:gthread.c, gthread_posix.c 參與編譯,生成 glib 庫;
  • Windows 系統:gthread.c, gthread_win32.c 參與編譯,生成 glib 庫;

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

2. 數據結構

你一定聽說過這個公式:程序 = 數據結構 + 算法,對于一個 C 語言項目,明白了數據結構的設計,對于理解整個程序的思路是非常重要的,在 glib 中也是如此。

glib 在設計線程庫的時候,分成 2 個層次:平臺無關部分,平臺相關部分。

平臺無關的數據結構有(一些不影響理解的代碼就刪掉了):

  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; 

平臺相關的數據結構有:

Linux 系統:

  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 系統:

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

仔細看一下每個結構體的第一個成員變量,是不是發現點什么?

從層次關系上看,這幾個結構體的關系為:

Linux 平臺:

Windows 平臺:

結構體在內存模型中意味著什么?占據一塊內存空間。

而這幾個數據結構都把"子"結構體,放在"父"結構體的第一個位置,就可以方便的進行強制類型轉換。

在以上內存模型中,GRealThread 結構體的第一部分是 GThread,那么就完全可以把 GRealThread 所處內存的開始部分,當做一個 GThread 結構體變量來操作。

用 C++ 中面向對象的術語來描述更準確:基類指針可以指向派生類對象。

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

3. 線程的創建

(1) 函數原型

平臺無關函數(gthread.c 中實現)

  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); 

平臺相關函數(gthread_posix.c or ghread_win32.c 中實現)

  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 平臺函數調用鏈

先來看一下 Linux 平臺上的函數調用關系:

如果你的手邊有源代碼,請關注 g_thread_new() 這個函數中的 func 和 data 這2個參數。

func 是最開始用戶層傳入的線程執行函數,也就是用戶創建這個線程,最終想執行的函數。data 是 func 函數所接收的函數參數。

如果直接面對 Linux 操作系統編程,在調用 POSIX 接口函數 pthread_create() 時,一般是直接傳入用戶想要執行的函數以及參數。

但是 glib 層并沒有直接把用戶層的函數直接交給 Linux 操作系統,而是自己提供了 2 個線程代理函數,在調用 pthread_create() 時,根據不同的情況,把這2個代理函數之一傳遞給操作系統:

第一個線程代理函數:g_thread_proxy();

第二個線程代理函數:linux_pthread_proxy();

至于傳遞哪一個代理函數,取決于宏定義 HAVE_SYS_SCHED_GETATTR 是否有效。

下面是 g_system_thread_new() 函數簡化后的代碼:

  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 字段,重點關注下面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. 線程的執行

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

當這個新建的線程被調度執行時,linux_pthread_proxy() 函數被調用執行:

簡化后的 linux_pthread_proxy() 函數:

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

這個函數關注 3 點:

  1. data 參數: 就是 g_system_thread_new 函數中的GThreadPosix類型指針,這是平臺相關的。
  2. 中間部分是設置線程屬性;
  3. 最后的 return 語句,調用了 glib 中第一個線程代理函數 g_thread_proxy。

繼續貼一下這個函數的簡化后代碼:

  1. gpointer 
  2. g_thread_proxy (gpointer data) 
  3.   // data 就是 g_system_thread_new 中 GThreadPosix 類型指針,這是平臺相關的。 
  4.   // 這里把它強轉成平臺無關的 GRealThread 類型。 
  5.   GRealThread* thread = data; 
  6.  
  7.   if (thread->name
  8.   { 
  9.       // 設置線程屬性:名稱 
  10.       g_system_thread_set_name (thread->name); 
  11.   } 
  12.  
  13.   // 調用應用層的線程入口函數 
  14.   thread->retval = thread->thread.func (thread->thread.data); 
  15.  
  16.   return NULL

這個函數也只要關注 3 點:

  1. data 參數: linux_pthread_proxy 函數傳過來的是 GThreadPosix 類型指針,但是這里直接賦值給了 GRealThread 類型的指針,因為它們的內存模型是包含的關系;
  2. 中間部分是設置線程名稱;
  3. 最后的 thread->thread.func (thread->thread.data) 語句,調用了用戶最開始傳入的函數并傳遞用戶的 data 參數。

至此,用戶層定義的線程函數 user_thread_func(data) 就得以執行了。

那么,如果 glib 層沒有定義宏 HAVE_SYS_SCHED_GETATTR,那么 Linux 系統中 pthread_create() 接收到的就是 glib 中的第一個線程代理函數 g_thread_proxy。

線程執行的調用關系為:

5. Windows平臺函數調用鏈

先來看一下 Windows 平臺上創建線程時函數調用關系:

在 Windows 平臺上,glib 的線程代理函數是 g_thread_win32_proxy()。

當這個新建的線程被調度執行時,函數調用關系是:

四、總結

實現這樣的線程函數代理設計,關鍵是利用了 C 語言中的結構體類型中,把“父”結構體類型變量強制轉換成“子”結構體類型變量來使用,因為它倆在內存模型中,剛開始部分的空間中,內容是完全一樣的。

最后,我把文中的這些圖合并起來,繪制成下面這 2 張圖,完整的體現了 glib 中的線程設計思路:

Linux 平臺:

Windows 平臺:

本文轉載自微信公眾號「IOT物聯網小鎮」,可以通過以下二維碼關注。轉載本文請聯系IOT物聯網小鎮公眾號。

 

責任編輯:武曉燕 來源: IOT物聯網小鎮
相關推薦

2010-05-24 19:31:48

SNMP服務

2010-05-24 19:28:59

SNMP服務

2020-03-05 10:36:12

云計算數據庫云平臺

2022-03-11 08:15:37

命令行工具指令

2014-03-12 10:00:26

移動開發跨平臺

2019-08-01 14:44:31

云存儲安全網絡

2010-05-19 13:27:58

IIS壓縮

2021-12-24 07:56:10

Cmake動態庫應用程序

2011-09-05 11:27:17

Sencha Touc框架HTML5

2011-06-21 14:01:07

QT 界面庫 Embedded

2009-03-23 09:05:01

2019-11-26 08:43:44

平臺桌面軟件

2011-04-06 17:24:43

MySQL數據庫復位根用戶密碼

2010-05-18 13:33:45

IIS管理

2010-04-27 17:14:36

AIX svmon

2012-01-18 11:25:36

服務器優化

2011-06-23 09:18:53

Python

2022-10-08 08:34:34

JVM加載機制代碼

2021-06-12 09:39:50

Python字典數據類型Python基礎

2021-05-27 05:22:28

前端引擎平臺
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 亚洲精品久久久一区二区三区 | 成人片免费看 | 亚洲毛片网站 | 国产精品一码二码三码在线 | 亚洲区一区二 | www.一级毛片 | 99re视频在线观看 | 亚洲精品福利在线 | 久久精品色视频 | 中文字幕欧美日韩 | 久久久91 | 污免费网站 | 国产精品区一区二区三 | 日韩欧美在线视频 | 欧美日韩国产在线观看 | 国产91久久精品一区二区 | 久久久久亚洲精品中文字幕 | 国产成人久久久 | 欧美激情国产日韩精品一区18 | 国产黄色在线观看 | 一级做a爰片性色毛片16美国 | 精品久久影院 | 羞羞视频在线观免费观看 | 精品视频一区二区三区在线观看 | 亚洲精品日韩在线 | 一区二区三区免费在线观看 | www.久久99| 日韩精品一二三 | 欧美不卡视频 | 我爱操| www.亚洲精品| 亚洲视频在线观看免费 | 日本免费黄色一级片 | 日韩欧美在线一区 | 欧美成人a∨高清免费观看 老司机午夜性大片 | 中文字幕一区二区三区不卡 | 一级做a爰片性色毛片视频停止 | 国产精品高清一区二区三区 | 夜夜爽夜夜操 | 久久久久综合 | 九九热re|