在不同的平臺上,有着若干不同的用於線程管理的接口。其中包括 POSIX pthreads 接口、Solaris 線程、Win32 線程等等。這些接口提供了相同或是相似的功能,但是它們的 API 的差別卻極爲懸殊。這就導致了困難、麻煩和易錯的編程,因爲應用程序員必須熟悉不同平臺上的若干接口。而且,這樣寫下的程序,是不可移植和不靈活的。
ACE_Thread提供了對 OS的線程調用的簡單包裝,這些調用處理線程創建、掛起、取消和刪除等問題。它提供給應用程序員一個簡單易用的接口,可以在不同的線程 API 間移植。ACE_Thread 是非常“瘦”的包裝,有着很少的開銷。其大多數方法都是內聯的,因而等價於對底層 OS專有線程接口的直接調用。ACE_Thread中的所有方法都是靜態的,而且該類一般不進行實例化。
下面的例子演示怎樣使用 ACE_Thread 包裝類創建、生成和聯接(join)線程。
#include "ace/Synch.h"
static int seed = 0;
{
ACE_UNUSED_ARG(arg);
ACE_DEBUG((LM_DEBUG,"Thread (%t) Created to do some work"));
::number++;
ACE_DEBUG((LM_DEBUG," and number is %d/n",::number));
//of time
ACE_OS::sleep(ACE_OS::rand()%2);
ACE_DEBUG((LM_DEBUG,
"/t/t Thread (%t) Done! /t The number is now: %d/n",number));
}
{
if (argc<2)
{
ACE_DEBUG((LM_DEBUG,"Usage: %s <number of threads>/n", argv[0]));
ACE_OS::exit(1);
}
int n_threads= ACE_OS::atoi(argv[1]);
ACE_thread_t *threadID = new ACE_thread_t[n_threads+1];
ACE_hthread_t *threadHandles = new ACE_hthread_t[n_threads+1];
if (ACE_Thread::spawn_n(threadID, //id!ˉs for each of the thread
n_threads, //number of threads to spawn
(ACE_THR_FUNC)worker, //entry point for new thread
0, //args to worker
THR_JOINABLE | THR_NEW_LWP, //flags
ACE_DEFAULT_THREAD_PRIORITY,
0, 0, threadHandles)==-1)
ACE_DEBUG((LM_DEBUG,"Error in spawning thread/n"));
for (int i=0; i<n_threads; i++)
ACE_Thread::join(threadHandles[i]);
//and have the process exit.
return 0;
}
一旦工作者函數啓動,它將全局變量 number 的值加 1,報告它的當前值,然後進入休眠狀態,以把處理器讓給其他線程。sleep()休眠一段隨機長度的時間。在線程醒來後,它將 number 的當前值通知給用戶,然後退出 worker()函數。
一旦線程從它的啓動函數返回,它在線程庫上隱含地發出線程 exit()調用並退出。這樣一旦“掉出”worker()函數,工作者線程也就退出了。負責創建工作者線程的主線程,在退出之前“等待”所有其他的線程完成它們的執行並退出。當主線程退出時(通過退出 main()函數),整個進程也將被銷燬。這之所以會發生是因爲每當線程退出 main()函數時,都會隱含地調用 exit(3c)函數。因此,如果主線程沒有被強制等待其他線程結束,當它死掉時,進程將會被自動銷燬,並在它的所有工作者線程完成工作之前銷燬它們!
上面所說的等待是通過使用 ACE_Thread::join()調用來完成的。該方法的參數是你想要主線程與之聯
接的線程的句柄(ACE_hthread_t)。
在此例中有若干事實值得注意。首先,在該類中沒有可供我們調用的管理功能,用以在內部記住應用所派生的線程的 ID。這使得我們難以聯接(join())、殺死(kill())或是一般性地管理我們派生的線程。在本章後面講述的 ACE_Thread_Manager 緩解了這些問題,一般說來,應該使用 ACE_Thread_Manager而不是線程包裝 API。
其次,在程序中沒有使用同步原語來保護全局數據。在此例中,它們並不是必須的,因爲所有的線
程都只對全局變量執行一次加操作。但是在實際應用中,爲了保護所有共享互斥數據(全局或靜態變量),比如說全局的 number變量,“鎖”將會是必需的。