使用 ACE 库框架在 UNIX 中开发高性能并发应用
Adaptive Communication Environment (ACE) 是一個高性能、開放源碼、面向對象的框架和 C++ 類庫,它有助于簡化網絡應用程序的開發。ACE 工具包包括一個操作系統層和一個封裝網絡 API 的 C++ 外觀(facades)集合。本文討論如何使用 ACE 線程設計高性能、并發、面向對象的網絡應用程序。對 ACE 的完整說明,包括如何下載和安裝這個工具包,請參見 參考資料。
用于創建和管理線程的 ACE 類
在進程中生成和管理多個線程涉及下面的類:
ACE_Thread_Manager:這是負責創建、管理和同步線程的主要的類。每種操作系統在處理線程方面有細微差異,這個類對應用程序開發人員隱藏這些差異。ACE_Sched_Params:使用這個類管理各個線程的調度優先級,調度優先級是在 ACE 源代碼發行版的 ace/Sched_Params.h 頭文件中定義的。可以采用不同的調度策略,可以是 “先到先服務” 的循環方式。
ACE_TSS:在多線程應用程序中使用全局變量會導致同步問題。ACE_TSS 類提供與線程相關的存儲模式,可以對那些對于程序是全局的,但是對于每個線程私有的數據提供抽象。ACE_TSS 類提供 operator() 方法,這個方法提供與線程相關的數據。
了解線程管理器類
原生操作系統線程 API 是不可移植的:存在語法和語義差異。例如,UNIX? pthread_create() 和 Windows? CreateThread() 方法都創建線程,但是語法不一樣。ACE_Thread_Manager 類提供以下功能:
它可以生成一個或更多線程,每個線程運行自己指定的函數。
它可以作為一個集合(稱為 線程組)管理相關的線程。
它管理各個線程的調度優先級。
它允許在線程之間進行同步。
它可以修改線程屬性,比如堆棧大小。
表 1. ACE_Thread_Manager 類的方法
方法名 說明
instance ? ACE_Thread_Manager 類是一個單實例類,使用這個方法訪問線程管理器的惟一實例。
spawn 這個方法創建一個新線程,它的一個輸入參數是 C/C++ 函數指針,這個函數執行應用程序的特定工作。
exit ? ? ? ??這個方法終止一個線程,釋放這個線程的所有資源。
spawn_n 這個方法創建屬于同一個線程組的多個線程。
close 這個方法關閉已經創建的所有線程并釋放它們的資源。
suspend 線程管理器暫停指定的線程。
resume 線程管理器恢復執行前面暫停的線程。
使用 ACE_Thread_Manager 類的變體
可以作為單實例類使用 ACE_Thread_Manager 類,也可以創建這個類的多個實例。對于單一實例,通過調用 instance 方法訪問實例。如果需要管理多個線程組,可以創建不同的線程管理器類,每個類控制它自己的線程集。清單 1. 使用 ACE_Thread_Manager 類創建一個線程
#include "ace/Thread_Manager.h"
#include <iostream>
void thread_start(void* arg)
{
? std::cout << "Running thread..\n";
}
int ACE_TMAIN (int argc, ACE_TCHAR* argv[])
{
? ACE_Thread_Manager::instance()->spawn((ACE_THR_FUNC)thread_start);
? return 0;
}
清單 2. ACE_Thread_Manager::spawn 方法的原型
int spawn (ACE_THR_FUNC func,
? ? ? ? ? ? ?void *arg = 0,
? ? ? ? ? ? ?long flags = THR_NEW_LWP | THR_JOINABLE | THR_INHERIT_SCHED,
? ? ? ? ? ? ?ACE_thread_t *t_id = 0,
? ? ? ? ? ? ?ACE_hthread_t *t_handle = 0,
? ? ? ? ? ? ?long priority = ACE_DEFAULT_THREAD_PRIORITY,
? ? ? ? ? ? ?int grp_id = -1,
? ? ? ? ? ? ?void *stack = 0,
? ? ? ? ? ? ?size_t stack_size = ACE_DEFAULT_THREAD_STACKSIZE,
? ? ? ? ? ? ?const char** thr_name = 0);
對于初學者來說,創建線程需要的參數數量似乎太多了,所以我們詳細討論一下各個參數和它們的作用:
ACE_THR_FUNC func:這是在生成線程時調用的函數。
void* arg:這是在生成線程時調用的函數的參數。void* 意味著用戶可以傳遞應用程序特有的任何數據類型,甚至可以使用某種結構把多個參數組合成單一數據。
long flags:使用 flags 變量設置生成的線程的幾個屬性。各個屬性都由單一位表示,按照 “或” 關系組合在一起。表 2 說明一些屬性。
ACE_thread_t *t_id:使用這個函數訪問創建的線程的 ID。每個線程具有惟一的 ID。
long priority:這是生成的線程的優先級。
int grp_id:如果提供這個參數,那么它表示生成的線程是否屬于現有的某一線程組。如果傳遞 -1,那么創建新的線程組并在這個組中添加生成的線程。
void* stack:這是預先分配的堆棧區域的指針。如果提供 0,就請求操作系統提供生成的線程的堆棧區域。
size_t stack_size:這個參數指定線程堆棧的大小(字節數)。如果對于前一個參數(堆棧指針)指定了 0,那么請求操作系統提供大小為 stack_size 的堆棧區域。
const char** thr_name:這個參數只與支持線程命名的平臺(比如 VxWorks)相關。對于 UNIX 平臺,在大多數情況下忽略它。
線程創建標志 說明
THR_CANCEL_DISABLE 不允許取消這個線程。
THR_CANCEL_ENABLE 允許取消這個線程。
THR_DETACHED ? ? ? ?創建異步線程。線程的退出狀態對于其他任何線程不可用。當線程終止時,操作系統回收線程資源。
THR_JOINABLE ? ? ? ?允許新線程的退出狀態對于其他線程可用。這也是 ACE 創建的線程的默認屬性。當這種線程終止時,操作系統不回收線程資源,直到其他線程聯結它為止。
THR_NEW_LWP 創建顯式的內核級線程(而不是用戶級線程)。
THR_SUSPENDED 創建處于暫停狀態的新線程。
清單 3. 使用 ACE_Thread_Manager 類創建多個線程?
#include "ace/Thread_Manager.h"
#include <iostream>
void print (void* args)
{
? int id = ACE_Thread_Manager::instance()->thr_self();
? std::cout << "Thread Id: " << id << std::endl;
}
int ACE_TMAIN (int argc, ACE_TCHAR* argv[])
{
? ACE_Thread_Manager::instance()->spawn_n(
? ? ? 4, (ACE_THR_FUNC) print, 0, THR_JOINABLE | THR_NEW_LWP);
? ACE_Thread_Manager::instance()->wait();
? return 0;
}
ACE 中的另一種線程創建機制
本節討論 ACE 提供的另一種線程創建/管理機制。這種方法不需要對線程管理器進行顯式的細粒度的控制。在默認情況下,每個進程在創建時有一個線程,這個線程在 main 函數開始時啟動,在 main 結束時終止。其他線程都需要顯式地創建。創建線程的另一種方式是創建預定義的 ACE_Task_Base 類的子類,然后覆蓋 svc() 方法。新線程在 svc() 方法中啟動,在 svc() 方法返回時終止。在進一步解釋之前,請看一下 清單 4 所示的源代碼。清單 4. 使用 ACE_Task_Base::svc 創建線程
#include “ace/Task.h”
class Thread_1 : public ACE_Task_Base {?
? public:?
? ? virtual int svc( ) {?
? ? ? ?std::cout << “In child’s thread\n”;
? ? ? ?return 0;
? ? }
?};
int main ( )
{?
? ?Thread_1 th1;
? ?th1.activate(THR_NEW_LWP|THR_JOINABLE);
? ?th1.wait();
? ?return 0;
}
在 svc() 方法中編寫與應用程序相關的線程行為。通過調用 activate() 方法(在 ACE_Task_Base 類中聲明和定義)執行線程。在激活線程之后,main() 函數等待子線程完成執行。這就是 wait() 方法的作用:在 Thread_1 執行完之前,主線程被阻塞。這一等待過程是必需的;否則,主線程會調度子線程并執行退出。在看到主線程退出時,C 運行時會銷毀所有子線程;因此,子線程可能根本沒有被調度或執行。
詳細了解 ACE_Task_Base 類
下面詳細看看 ACE_Task_Base 中的幾個方法。
清單 5 給出 activate() 方法的原型。
清單 5. ACE_Task_Base::activate 方法的原型
virtual int activate (long flags = THR_NEW_LWP | THR_JOINABLE | THR_INHERIT_SCHED,
? ? ? ? ? ? ? ? ? ? ? ? int n_threads = 1,
? ? ? ? ? ? ? ? ? ? ? ? int force_active = 0,
? ? ? ? ? ? ? ? ? ? ? ? long priority = ACE_DEFAULT_THREAD_PRIORITY,
? ? ? ? ? ? ? ? ? ? ? ? int grp_id = -1,
? ? ? ? ? ? ? ? ? ? ? ? ACE_Task_Base *task = 0,
? ? ? ? ? ? ? ? ? ? ? ? ACE_hthread_t thread_handles[ ] = 0,
? ? ? ? ? ? ? ? ? ? ? ? void *stack[ ] = 0,
? ? ? ? ? ? ? ? ? ? ? ? size_t stack_size[ ] = 0,
? ? ? ? ? ? ? ? ? ? ? ? ACE_thread_t thread_ids[ ] = 0,
? ? ? ? ? ? ? ? ? ? ? ? const char* thr_name[ ] = 0);
可以使用 activate() 方法創建一個或多個線程,每個線程調用相同的 svc() 方法,所有線程采用相同的優先級并具有相同的組 ID。下面簡要介紹一些輸入參數:
long flags:參見 表 2。
int n_threads:n_threads 指定要創建的線程的數量。
int force_active:如果這個標志是 True,而且存在這個任務已經生成的線程,那么新生成的所有線程會共享以前生成的線程的組 ID,忽略傳遞給 activate() 方法的值。
long priority:這個參數指定線程或線程集合的優先級。調度優先級值是與操作系統相關的,堅持使用默認值 ACE_DEFAULT_THREAD_PRIORITY 是最安全的。
ACE_hthread_t thread_handles:如果 thread_handles 不是零,那么在生成 n 個線程之后,會把各個線程句柄賦值給這個數組。
void* stack:如果指定這個參數,它指定一個指針數組,這些指針指向各個線程的堆棧基。
size_t stack_size:如果指定這個參數,它指定一個整數數組,這些整數表示各個線程堆棧的大小。
ACE_thread_t thread_ids:如果 thread_ids 不是零,那么這個參數是一個數組,其中包含 n 個新生成的線程的 ID。
清單 6 給出 ACE_Task_Base 類中另外幾個有用的例程。
清單 6. ACE_Task_Base 中的其他例程
// Block the main thread until all threads of this task are completed?
virtual int wait (void);
// Suspend a task
virtual int suspend (void);
// Resume a suspended task.
virtual int resume (void);
// Gets the no. of active threads within the task
size_t thread_count (void) const;
// Returns the id of the last thread whose exit caused the thread count?
// of this task to 0. A zero return status implies that the result is?
// unknown. Maybe no threads are scheduled.
ACE_thread_t last_thread (void) const;
為了創建處于暫停狀態的線程(而不是通過調用 suspend() 方法顯式地暫停),需要向 activate() 方法傳遞 THR_SUSPENDED 標志。可以通過調用 resume() 方法恢復執行線程,見 清單 7。
清單 7. 暫停線程和恢復執行
Thread_1 th1;
th1.activate(THR_NEW_LWP|THR_JOINABLE|THR_SUSPENDED);
…// code in the main thread
th1.resume();
…// code continues in main thread
再看看線程標志
有兩種線程:內核級線程和用戶級線程。如果不帶任何參數調用 activate() 方法,那么默認情況下創建內核級線程。內核級線程與操作系統直接交互,由內核級調度器調度。THR_NEW_LWP標志(activate() 方法的默認參數)總是確保新創建的線程是內核級線程。
與此相反,用戶級線程在進程范圍內運行,為了完成某些任務,根據需要 “分配” 內核級線程。
線程鉤子
線程鉤子
ACE 提供一個全局的線程啟動鉤子,這允許用戶執行可以應用于所有線程的任何操作。
為了創建啟動鉤子,需要創建預定義類 ACE_Thread_Hook 的子類并提供 start() 方法定義。start() 接受兩個參數:一個用戶定義函數的指針和傳遞給這個用戶定義函數的 void*。為了注冊鉤子,需要調用靜態方法 ACE_Thread_Hook::thread_hook,見 清單 8。
清單 8. 使用全局線程鉤子
#include "ace/Task.h"
#include "ace/Thread_Hook.h"
#include <iostream>
class globalHook : public ACE_Thread_Hook {
? public:
? ? virtual ACE_THR_FUNC_RETURN start (ACE_THR_FUNC func, void* arg) {
? ? ? ? std::cout << "In created thread\n";
? ? ? ? (*func)(arg);
? ? }
};
class Thread_1 : public ACE_Task_Base {
? public:
? ? virtual int svc( ) {
? ? ? ?std::cout << "In child's thread\n";
? ? ? ?return 0;
? ? }
?};
int ACE_TMAIN (int argc, ACE_TCHAR* argv[])
{
? globalHook g;
? ACE_Thread_Hook::thread_hook(&g);
? Thread_1 th1;
? th1.activate();
? th1.wait();
? return 0;
}
注意,自動傳遞給啟動鉤子的 ACE_THR_FUNC 指針是在執行線程的 activate() 方法時調用的相同函數。以上代碼的輸出是:
In created thread
In child’s thread
結束語
本文簡要討論了如何使用 ACE 框架創建和管理線程。ACE 框架還有其他一些有用的特性,比如互斥、用于同步的保護阻塞、共享內存和網絡服務。詳細信息請參見 參考資料。(責任編輯:A6)
總結
以上是生活随笔為你收集整理的使用 ACE 库框架在 UNIX 中开发高性能并发应用的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ACE网络编程模式比较
- 下一篇: ACE源代码目录结构