2017年3月30日 星期四

Thread簡介

Thread簡介

在同一個程式中的每個thread會共用全域記憶體, 包括
  • 初始化資料(initialized data)
  • 未初始化資料(uninitialized data)
  • heap segment
使用fork()的問題:
  • process之間資訊較難以共用
  • 使用fork()建立process成本較高, 需要複製如page table和file descriptor table之類的各種process屬性
改用thread可以改善這些問題
  • thread之間可以簡便快速的分享資訊, 只需要將資料複製共用變數中.
  • fork()建立sub process需要複製許多屬性,  而thread本來就共用這些屬性

Thread共用屬性和各自有的屬性:

共用屬性:
  • process ID 和 parent process ID
  • process群組id和作業階段id(session ID)
  • 控制終端機(controlling terminal)
  • 行程憑證(process credential)(使用者ID與群組ID)
  • 開啟的file descriptors
  • 使用fcntl()建立的recording lock(紀錄鎖)
  • signal處理
  • 檔案系統的相關資訊: 權限遮罩(umask), 目前工作目錄和根目錄
  • 間隔計時器(setitimer())與POSIX計時器(timer_create())
  • System V(system v)號誌undo(semadj)
  • 資源限制(resource limit)
  • CPU時間消耗(由times()傳回)
  • 資源消耗(由getrusage()傳回)
  • nice值(由setpriority()nice()設定)

各自擁有的屬性:
  • thread ID
  • 訊號遮罩(signal mask)
  • 執行緒特有的資料
  • 替代的訊號堆疊(sigaltstack())
  • errno變數
  • 浮點(floating-point)環境
  • 即時排班策略(real-time scheduling policy)和優先權
  • CPU affinity
  • 能力(capability)
  • 堆疊(stack), 區域變數和函式的呼叫連結

Pthread API簡介:
資料型別
說明
pthread_t
  • Thread ID
  • Linux將其定義為無號長整數型別(unsigned long)
  • Linux實作中 ThreadID在全部的process中也是唯一值
pthread_mutex_t
互斥(Mutex)
pthread_mutexattr_t
互斥屬性物件
pthread_cond_t
條件變數(condition variable)
pthread_condattr_t
條件變數的屬性物件
pthread_key_t
Thread特有的資料鍵(Key)
pthread_once_t
一次性的初始化控制上下文(One-time initialization control context)
pthread_attr_t
Thread的屬性物件


中止Thread的方式
  • thread的start函式return並傳回指定回傳值
  • thread呼叫pthread_exit()
  • 使用pthread_cancel()取消thread
  • 任何呼叫exit()的thread或主thread進行return都會導致process中的所有threads全部終止

Pthread函式操作

pthread_create()
int pthread_create(pthread_t *thread, const pthread_atr_t *attr, void *(*start)(volid *    ), void *arg)
  • 產生一個新的thread
  • pthread_create()的thread會去執行start()函式, arg則是要傳給start函式的參數
  • 通常arg是指向一個全域變數或堆積(heap)變數, 也可以設為NULL
  • 傳遞多個arg給start()可以將arg指向一個struct的指標, 將參數放在該struct各欄位
  • thread是一個buffer用來存threadID
  • attr用來指定新thread的各種屬性, NULL為預設值

pthread_exit()
void pthread_exit(void *retval)
  • 終止呼叫pthread_exit()的thread
  • 呼叫pthread_exit()等同於在thread的start()中呼叫return
  • retval指定thread的回傳值, 並建議retval所指定值不應該配置在stack中
  • 主thread呼叫了pthread_exit()而非exit()或return, 則其他thread會繼續執行

pthread_self()
pthread_t pthread_self(void)
  • 取得自己的threadID

pthread_equal()
int pthread_equal(pthread_t t1, pthread_t t2)
  • 檢查兩個threadID是否相同
  • 回傳非0表示相同, 回傳0表示不同

pthread_join()
int pthread_join(pthread_t thread, void **retval)
  • 此函式會等待為threadID的thread終止, 此操作稱為joining(併入)
  • 若retval不為NULL會儲存終止的thread回傳值得副本. 此回傳值就是thread return或呼叫pthread_exit()所指定的值
  • 不可重複joining同一個thread, 會發生無法預期的情形
  • thread尚未分離(detached)若我們joining thread失敗的話會產生zombie thread. zombie thread多到一定數量就無法在產生新的thread

pthread_detach()
int pthread_detach(pthread_t thread)
  • 不需要thread的回傳狀態, 只需要系統能在thread終止時去回收並清理thread.可以使用pthread_detach()將thread標示成detached狀態
  • thread可以對自己標示成detached狀態
    • pthread_deatch(pthread_self())
  • 標示成detached的thread無法被其他thread使用pthread_join()來取得thread狀態
  • 若其他thread呼叫exit(), detached thread仍會被終止

Thread屬性的使用範例
pthread_t thr;
pthread_attr_t attr;
int s;
s = pthread_attr_init(&attr);       /* Assigns default values */
if (s != 0)
   errExitEN(s, "pthread_attr_init");
s = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
if (s != 0)
   errExitEN(s, "pthread_attr_setdetachstate");

s = pthread_create(&thr, &attr, threadFunc, (void *) 1);
if (s != 0)
   errExitEN(s, "pthread_create");

s = pthread_attr_destroy(&attr);    /* No longer needed */
if (s != 0)
   errExitEN(s, "pthread_attr_destroy");

s = pthread_join(thr, NULL);
if (s != 0)
   errExitEN(s, "pthread_join failed as expected");