pthread小結

參考1 https://computing.llnl.gov/tutorials/pthreads/
參考2 http://man7.org/linux/man-pages/man7/pthreads.7.htmlhtml


join

  • int pthread_join(pthread_t, void**);阻塞調用線程,直至指定pthread_t線程終止
  • 在同一個線程中重複調用join會致使錯誤
  • 在建立線程的時候能夠指定要建立的線程是否joinable,若是是,則能夠join,不然(即detached)不能夠。通常默認都是joinable
  • POSIX指出線程should指定爲joinable
  • 若是肯定一個線程須要join,那麼最好明確指定該線程joinable,經過以下四步:
  1. Declare a pthread attribute variable of the pthread_attr_t data type
  2. Initialize the attribute variable with pthread_attr_init()
  3. Set the attribute detached status with pthread_attr_setdetachstate()
  4. When done, free library resources used by the attribute with pthread_attr_destroy()
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
for(t=0; t<NUM_THREADS; t++) {
   printf("Main: creating thread %ld\n", t);
   rc = pthread_create(&thread[t], &attr, BusyWork, (void *)t);  //一個attr能夠給多個線程使用
   if (rc) {
      printf("ERROR; return code from pthread_create() is %d\n", rc);
      exit(-1);
      }
   }
pthread_attr_destroy(&attr);    //記得釋放資源。create執行完以後就能夠釋放,而不用等待線程結束
  • 若是肯定一個線程不須要joinable,那麼應該明確考慮設置屬性爲detached
  • 經過pthread_detach()來設置線程爲不可join,即便它被建立的時候被設置爲joinable。這個動做不可逆

    stack

  • POSIX沒有規定建立的線程的stack大小是多少,這是由implementation決定的
  • pthread_attr_setstacksize能夠用來設置須要的stack大小
  • pthread_attr_getstackaddrpthread_attr_setstackaddr能夠用來設置stack須要放置到特定的內存區域
size_t stacksize;
pthread_attr_init(&attr);
pthread_attr_getstacksize (&attr, &stacksize);
printf("Default stack size = %li\n", stacksize);
size = 10000; //設置爲10000bytes
pthread_attr_setstacksize (&attr, stacksize);
printf("set stack size = %li\n", stacksize);
pthread_create(&threads[t], &attr, dowork, (void *)t);

mutex

Creating and Destroying Mutexes

//destroy,成功則返回0
int pthread_mutex_destroy(pthread_mutex_t *mutex);
//動態初始化,成功則返回0. 若是attr爲NULL,那麼將使用默認屬性,至關於PTHREAD_MUTEX_INITIALIZER
int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);
//使用默認參數靜態初始化
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

//mutex屬性
int pthread_mutexattr_destroy(pthread_mutexattr_t *attr);
int pthread_mutexattr_init(pthread_mutexattr_t *attr);
  • 被destroy的mutex可使用pthread_mutex_init從新初始化
  • destroy一個處於lock狀態的mutex,將會致使undefined行爲
  • 只有mutex能夠用來執行synchronization,用它的copies來執行lock,unlock和trylock將致使undefined
  • 不能夠重複初始化已經初始化了的mutexlinux

    Locking and Unlocking Mutexes

//若是別的線程已經lock,那會一直阻塞當前線程直至得到鎖
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
mutex類型 性質
PTHREAD_MUTEX_NORMAL 對mutex的重複lock,即本線程已經lock了mutex,在沒有unlock以前又嘗試lock,將致使死鎖行爲;unlock一個沒有被本線程lock或者沒有被任何線程lock的mutex,將致使未定義行爲
PTHREAD_MUTEX_ERRORCHECK 嘗試重複lock一個mutex將不會死鎖,而是返回一個錯誤值;unlock一個沒有被本線程lock或者沒有被任何線程lock的mutex,也會返回錯誤值
PTHREAD_MUTEX_RECURSIVE mutex能夠被重複lock。每次lock會增長相關計數,直至經過unlock使計數達到0時,才能夠被別的線程lock;unlock一個沒有被本線程lock或者沒有被任何線程lock的mutex,也會返回錯誤值
PTHREAD_MUTEX_DEFAULT 重複lock會致使未定義行爲(NORMAL中會致使死鎖);unlock一個沒有被本線程lock或者沒有被任何線程lock的mutex,也將致使未定義行爲。 不過,在NDK的定義中,直接把PTHREAD_MUTEX_DEFAULT = PTHREAD_MUTEX_NORMAL
  • pthread_mutex_trylockpthread_mutex_lock只有一點區別:若是當前mutex被任意線程lock,pthread_mutex_trylock都將會馬上返回。若是mutex是PTHREAD_MUTEX_RECURSIVE的,且mutex已經被當前調用線程lock,pthread_mutex_trylock也一樣會致使計數增一,並返回success。
  • 若是正在等待lock的線程收到了一個signal,當其從signal handler返回以後,會繼續等待lock,就和signal沒有發生同樣
  • 除非使用了 thread priority scheduling,不然多個正在等待lock的線程得到lock的狀況可能多少有點random
  • 若是成功,這三個函數都是返回0,不然返回相應的error

Condition Variables

  • mutex經過控制對數據的訪問權限來達到同步;而condition variables則基於數據的值來控制同步
  • 若是不使用condition variable,線程想要檢查某個條件則只能經過輪詢的方式,這將很是resource consuming,由於這期間線程將一直active。而使用condition variable則將在不使用輪詢的狀況下實現此目標
  • condition variable 常常和mutex一塊兒使用dom

    Creating and Destroying Condition Variables

int pthread_cond_destroy(pthread_cond_t *cond);
int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr);
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

int pthread_condattr_destroy(pthread_condattr_t *attr);
int pthread_condattr_init(pthread_condattr_t *attr);
  • destory正由某個線程用於block的cv將致使未定義行爲
  • 只有cv本身可以用於同步,任何基於它的copies調用pthread_cond_wait(), pthread_cond_timedwait(), pthread_cond_signal(), pthread_cond_broadcast(), pthread_cond_destroy()都會產生未定義行爲
  • 初始化一個已經初始化的cv會致使未定義行爲;已經destory的cv能夠再次初始化;

Waiting and Signaling on Condition Variables

通常使用流程:
FueOfg.jpg函數

int pthread_cond_timedwait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex, 
                            const struct timespec *restrict abstime);
int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex);
  • 這兩個函數將致使調用線程block on the condition variable, 而且須要傳入一個由調用線程lock了的mutex,不然致使未定義行爲
  • 若是在wait以前,沒有明確lock對應mutex,可能並不會致使block
  • 這兩個函數會原子的unlock the mutex,而且致使調用線程block on the condition variable。這裏的原子的意味着:只要其餘線程lock了這個mutex,那麼這個線程對pthread_cond_broadcast()pthread_cond_signal()的調用都會產生調用wait的線程已經blocked on the condition variable的效果
  • 只要這兩個函數返回,那麼調用線程就已經lock了這個mutex
  • 虛假喚醒Spurious wakeups 可能會產生,並且這並不違反標準,因此,即便調用線程被喚醒,也不意味着對某個值作出某種保證,應該再次確認條件是否真的知足了。同時,考慮到線程之間的競爭,pthread_cond_timedwait因爲超時返回以後,條件也可能已經知足。總之。任什麼時候候wait返回,都須要從新評估條件是否知足,這點很是重要
  • 一旦線程waits on the condition variable,那麼這個cv就和相應的mutex綁定了,在wait返回以前,不能再使用另外的mutex來調用wait,這會致使未定義行爲
  • condition wait是一個cancellation point未明白
  • 假設一個因爲wait調用而block線程因爲被canceled而unblocked,這個不會consume任何condition signal。
  • pthread_cond_timedwait()pthread_cond_wait()是equivalent的,除了:當signaled或者broadcasted超過指定時間,pthread_cond_timedwait()就會返回返回error。同時,cv還能夠支持 Clock Selection,選擇不一樣的Clock來measure指定的時間
  • 當cv wait期間,一個signal產生了,那麼cv可能會繼續wait就像沒有中斷同樣,或者這會造成一個spurious wakeup,返回0.
    推薦使用while循環替代if語句來檢查當前條件是否真的知足,有以下三點好處:
  1. 若是有多個線程都是在wait相同過的wake up signal,那麼當其餘任意一個被waked up以後,他們都有可能更改條件值,而致使條件不知足
  2. 線程可能會由於程序bug而收到一個signal
  3. Pthreads library被允許產生虛假喚醒,並且這並不違反標準
int pthread_cond_broadcast(pthread_cond_t *cond);
int pthread_cond_signal(pthread_cond_t *cond);
  • 當多於一個線程在wait的時候,應該使用pthread_cond_broadcast()
  • 在signal以前,應該先lock對應mutex,而後在signal以後,應該unlock對應mutex。若是忘記了unlock,那麼相應的wait線程會繼續blocked,由於他們沒法得到lock

結束線程

有:ui

  • pthread_cancel
  • pthread_exit
  • pthread_kill
    參考https://www.cnblogs.com/biyeymyhjob/archive/2012/10/11/2720377.html
    ---線程

    其餘函數

    pthread_self

    pthread_t pthread_self(void);返回調用線程的thread idrest

    pthread_equal

    int pthread_equal(pthread_t t1, pthread_t t2);比較兩個ID是否相等,若是相等則返回not-zero value不相等則返回0。因爲pthread_t結果opaque,因此不該該用==來比較code

    pthread_once

    int pthread_once(pthread_once_t *once_control, void (*init_routine)(void));:在進程中,任何首次調用這個函數的線程,在pthread_once_t once_control = PTHREAD_ONCE_INIT的時候,會調用init_routine程序。而且當此函數返回的時候,init_routine已經執行完了(這裏沒有說init_routine會阻塞調用線程,可能考慮的是,當線程A已經調用init_routine,而另一個線程B也調用了pthread_once,那麼是否B也會等待A調用的init_routine執行完畢?)。若是成功完成,則pthread_once返回0。若是once_control參數不是PTHREAD_ONCE_INIT,那麼行爲將是undefined。在LinuxThreads中:htm

在LinuxThreads中,實際"一次性函數"的執行狀態有三種:NEVER(0)、IN_PROGRESS(1)、DONE (2),若是once初值設爲1,則因爲全部pthread_once()都必須等待其中一個激發"已執行一次"信號,所以全部pthread_once ()都會陷入永久的等待中;若是設爲2,則表示該函數已執行過一次,從而全部pthread_once()都會當即返回0。blog

這個函數在當沒法編輯進程的main函數,好比寫一個庫的時候,就頗有用。
TODO:若是多個線程使用的init_routine不相同怎麼辦?或者好比本身開發庫,可是user的main中已經使用不一樣的init_routine調用了pthread_once,那麼會是什麼結果?

相關文章
相關標籤/搜索