pthread 學習

1. 建立線程api

  int pthread_create (pthread_t* thread, pthread_attr_t* attr, void* (*start_routine)(void*), void* arg);數組

  thread:線程句柄,用於標示一個線程;安全

  attr(線程屬性結構):函數

  1.__detachstate(同步狀態):性能

    PTHREAD_CREATE_JOINABLE:結合的狀態,在主線程退出以前,應該等待子線程完成;測試

    PTHREAD_CREATE_DETACH:分離的狀態,和主線程分離,本身運行結束後並釋放資源;spa

  2.__schedpolicy(調度策略):線程

    SCHED_OTHER:正常、非實時,默認狀態unix

    SCHED_RR:實時、輪轉法code

    SCHED_FIFO:實時、先入先出

  3.__schedparam:目前只有一個sched_priority(調度優先級)屬性

  4.__inheritsched:

    PTHREAD_EXPLICIT_SCHED:顯式指定調度策略和調度參數;

    PTHREAD_INHERIT_SCHED:繼承調用線程的參數;

  5.__scope:

    PTHREAD_SCOPE_SYSTEM:與系統中全部線程一塊兒競爭CPU時間,默認值;

    PTHREAD_SCOPE_PROCESS:與同進程中的線程競爭CPU;

  start_routine:線程啓動函數;

  arg: 線程啓動的時候,傳遞給啓動函數的參數,若是沒有就傳入0;

設置屬性的一些api:

  pthread_attr_init(pthread_attr_t*)

  pthread_attr_destroy(pthread_attr_t*)

  pthread_attr_set-/get- 一系列函數

 

2. 線程的取消

一個線程能夠向另外一個線程發送取消請求,來終止另外一個線程的執行,不過另外一個線程能夠設置是否忽略取消請求,若是在不忽略的狀況下,根據線程的設置,多是執行到下一個取消點(Cancelation-point)才被終止執行,也多是當即終止執行。

線程的取消點:

  pthread_join()、pthread_testcancel()、pthread_cond_wait()、pthread_cond_timedwait()、sem_wait()、sigwait()等函數以及read()、write()會引發系統阻塞的函數都是取消點。

  對於沒有取消點的執行線程,咱們能夠在須要被取消執行的地方調用pthread_testcancel(),當一個線程接受到取消請求並無忽略的狀況下,一旦執行到pthread_testcancel()就會終止執行。

API:

int pthread_cancel(pthread_t); 向一個線程發送取消請求,成功返回0,不然返回非0,成功發送不表明已經完成取消;

int pthread_setcancelstate(int state, int *oldstate); 設置取消狀態,有PTHREAD_CANCEL_ENABLE(缺省)和PTHREAD_CANCEL_DISABLE兩種,第一種是接受取消請求,第二種是忽略取消請求。oldstate若是不爲0,用於存入以前的取消狀態,以便未來能夠恢復。

int pthread_setcanceltype(int type, int *oldtype); 設置取消動做的時機,有PTHREAD_CANCEL_DEFFERED和PTHREAD_CANCEL_ASYCHRONOUS兩種,第一種是執行到下一個取消點終止,第二種是當即終止執行。oldtype若是不爲0,用於存入以前的值,以便未來能夠恢復。

注:在unix環境下測試,兩種狀況都是運行到下一個取消點或者有pthread_testcancel()的位置才終止,不清楚具體緣由。

 

3. 互斥鎖

  因爲線程具備搶佔式和共享資源的特色,所以須要用一種互斥機制來保證線程之間的同步,最經常使用的就是互斥鎖。

建立互斥鎖:

  靜態方式:pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

  動態方式:int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr); mutexattr缺省值爲0;

銷燬互斥鎖:

  int pthread_mutex_destroy(pthread_mutex_t *mutex); 銷燬鎖佔用的資源,若是鎖定狀態則返回EBUSY;

鎖類型,在pthread_mutexattr_t(屬性)中指定:

  PTHREAD_MUTEX_TIMED_NP:普通鎖(缺省值)。當一個線程加鎖之後,其他請求鎖的線程將造成一個等待隊列,並在解鎖後按優先級得到鎖;

  PTHREAD_MUTEX_RECURSIVE_NP:嵌套鎖。容許同一個線程對同一個鎖成功得到屢次,並經過屢次unlock解鎖。若是是不一樣線程請求,則在加鎖線程解鎖時從新競爭;

  PTHREAD_MUTEX_ERRORCHECK_NP:檢錯鎖。若是同一個線程請求同一個鎖,則返回EDEADLK,不然與PTHREAD_MUTEX_TIMED_NP類型動做相同。這樣就保證當不容許屢次加鎖時不會出現最簡單狀況下的死鎖;

  PTHREAD_MUTEX_ADAPTIVE_NP:適應鎖。動做最簡單的鎖類型,僅等待解鎖後從新競爭;

鎖操做:

  不管是什麼類型的鎖,都只能被一個線程獲取,不可能出現被兩個線程同時獲取到鎖的狀況。

  普通鎖和適應鎖,解鎖者能夠是任何線程;

  檢錯鎖則必須由加鎖者解鎖纔有效,不然返回EPERM;

  嵌套鎖,文檔和實現要求必須由加鎖者解鎖;

  int pthread_mutex_lock(pthread_mutex_t *mutex); 請求上鎖,若是沒有獲取到鎖,線程會被阻塞;

  int pthread_mutex_unlock(pthread_mutex_t *mutex); 解鎖;

  int pthread_mutex_trylock(pthread_mutex_t *mutex); 請求上鎖,若是成功得到一個鎖,返回0,不然返回非0,該函數當即返回,不會阻塞;

鎖機制並非線程安全的,若是在線程上鎖以後,解鎖以前,被取消,就可能出現死鎖的狀況。這種狀況須要使用線程退出回調函數來解鎖。 

 

4. 條件變量

  線程上鎖以後,應該儘量快的解鎖,好讓其餘線程能夠快速獲取鎖,以便提升程序性能。但不管如何,相關線程都會頻繁的循環檢測鎖狀態以便獲取鎖,例如可能在某種狀況下,某個線程會在大部分時間上鎖失敗,這樣不只消耗了cpu週期,並且也是無心義的檢測。所以,條件變量很好的改善了這種狀況。

  條件變量是一種線程間的同步機制。主要利用了一個共享的條件變量來進行同步,一個線程等待條件變量進行掛起,而一個線程在條件成立的時候,喚醒阻塞的線程。爲了防止競爭和對資源的保護,條件變量老是和互斥鎖一塊兒使用。

建立條件變量:

  靜態方式:pthread_cond_t cond=PTHREAD_COND_INITIALIZER;

  動態方式:int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *cond_attr);

  int pthread_cond_destroy(pthread_cond_t *cond);

    銷燬條件變量,若是條件變量在被等待,返回EBUSY;

使用條件變量:

  int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);

    首先對mutex解鎖(所以在調用pthread_cond_wait前,mutex必須處於鎖定狀態),而後等待cond的喚醒,此時該函數並不會當即返回而是被阻塞,直到cond被喚醒,而後對mutex上鎖,該函數返回。

  int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime);

    和pthread_cond_wait相似,只是最有一個參數代表在規定時刻前條件沒有成立,返回ETIMEOUT,並結束等待。

  int pthread_cond_signal(pthread_cond_t*); 

    喚醒隊列中第一個線程;

  int pthread_cond_broadcast();

    喚醒全部等待條件變量的線程;

因爲wait操做也是一個取消點,所以在wait操做結束以後,若是線程執行了取消操做,那麼mutex會被一直鎖定,形成死鎖。這情狀況須要使用退出回調函數來解鎖。

  

5. 終止線程

  正常終止:函數調用return和執行pthread_exit(pthread_t),這種是代碼能夠控制的終止狀況;

  異常終止:被其餘線程取消,或者線程執行發生異常;

  異常終止可能會致使資源不能被正常釋放,其中包括鎖,若是鎖資源被解鎖前,線程就異常終止了,那麼其餘線程將永遠沒法再得到該鎖。

線程終止的清理:

  使用pthread提供的清理函數(因爲清理函數是宏定義,必須在同一層做用域成對出現,才能編譯經過):

  void pthread_cleanup_push(void (*routine) (void *), void *arg);

    入棧一個清理函數和須要清理的對象;

  void pthread_cleanup_pop(int execute);

    在線程退出的時候,執行棧中的清理函數;

  清理函數會在自動釋放資源,出如今函數對中的代碼段的任何終止動做,都將執行清理函數。

  若是須要在線程終止時,自動釋放鎖資源,可使用:

    pthread_cleanup_push(pthread_mutex_unlock, (void *) &mut);

    pthread_mutex_lock(&mut);

    ......

    pthread_mutex_unlock(&mut);

    pthread_cleanup_pop(0);

線程的終止:

  void pthread_exit(void *retval);

    線程終止本身的執行,並清理資源。

  int pthread_join(pthread_t th, void **thread_return);

    合併線程,若是是joinable的子線程,一般在主線程結束以前,必須調用該函數等待子線程的執行完成,然和合併到主線程。thread_return若是不爲0,保存線程的返回值。

  int pthread_detach(pthread_t th);

    分離線程,若是是detachable的子線程,它會在本身執行完成後,自動清理資源,主線程不須要等待它完成;

 

5. 線程私有數據(TSD)

  int pthread_key_create(pthread_key_t *key, void (*destr_function) (void *));

  該函數從TSD池中分配一項,將其值賦給key供之後訪問使用。若是destr_function不爲空,在線程退出(pthread_exit())時將以key所關聯的數據爲參數調用destr_function(),以釋放分配的緩衝區。

  不論哪一個線程調用pthread_key_create(),所建立的key都是全部線程可訪問的,但各個線程可根據本身的須要往key中填入不一樣的值,這就至關於提供了一個同名而不一樣值的全局變量。

  TSD池用一個結構數組表示:

    static struct pthread_key_struct pthread_keys[PTHREAD_KEYS_MAX] = { { 0, NULL } };

  建立一個TSD就至關於將結構數組中的某一項設置爲"in_use",並將其索引返回給*key,而後設置destructor函數爲destr_function。

  int pthread_key_delete(pthread_key_t key);

  這個函數並不檢查當前是否有線程正使用該TSD,也不會調用清理函數(destr_function),而只是將TSD釋放以供下一次調用pthread_key_create()使用。在LinuxThreads中,它還會將與之相關的線程數據項設爲NULL(見"訪問")。

  int pthread_setspecific(pthread_key_t key, const void *pointer);

  void * pthread_getspecific(pthread_key_t key);

  寫入(pthread_setspecific())時,將pointer的值(不是所指的內容)與key相關聯,而相應的讀出函數則將與key相關聯的數據讀出來。數據類型都設爲void *,所以能夠指向任何類型的數據。

 

實例:

//
//  main.c
//  threads
//
//  Created by avl-showell on 16/7/13.
//  Copyright © 2016年 avl-showell. All rights reserved.
//

#include <stdio.h>
#include <unistd.h>
#include "pthread.h"

pthread_t thread_1, thread_2, thread_3;
pthread_cond_t cond;
pthread_mutex_t mutex;
pthread_key_t key;

void* thread_1_func (void* arg) {
    static int i = 0;
    
    const char* data = "tread1";
    pthread_setspecific(key, data);
    
    pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, 0);
    pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, 0);
    
    pthread_cleanup_push(pthread_mutex_unlock, &mutex);
    
    while (1) {
        printf("thread 1 wait %s \n", pthread_getspecific(key));
        
        //pthread_cond_wait(&cond, &mutex);
        int state = pthread_mutex_trylock(&mutex);
            if (state == 0) {
            
            printf("thread 1 start \n");
            
            //sleep(1);
            
            pthread_testcancel();
            
            i++;
            
            printf("thread 1 end %d \n", i);
            
            pthread_mutex_unlock(&mutex);
        }
        else {
            printf("thread 1 get lock failed... \n");
        }
    }
    
    pthread_cleanup_pop(0);
    
    return 0;
}

void* thread_2_func (void* arg) {
    static int i = 0;
    
    const char* data = "tread2";
    pthread_setspecific(key, data);
    
    while (1) {
        pthread_mutex_lock(&mutex);
        
        printf("thread 2 wait %s \n", pthread_getspecific(key));
        
        pthread_cond_wait(&cond, &mutex);
        
        printf("thread 2 start \n");
        
        //sleep(1);
        
        if (i == 0) {
            pthread_cancel(thread_1);
        }
        if (i == 8) {
            pthread_mutex_unlock(&mutex);
            pthread_exit(0);
        }
        i++;
        
        printf("thread 2 end %d \n", i);
        
        pthread_mutex_unlock(&mutex);
    }
    
    return 0;
}

void* thread_3_func (void* arg) {
    static int i = 1;
    
    while (1) {
        printf("thread 3 start \n");
        
        sleep(1);
        
        pthread_cond_signal(&cond);
        
        if (i == 20)
            break;
        i++;
        
        printf("thread 3 end \n");
    }
    
    return 0;
}

void destroy (void* arg) {
    printf("pthread destroy key \n");
}

int main(int argc, const char * argv[]) {
    pthread_cond_init(&cond, 0);
    pthread_mutex_init(&mutex, 0);
    pthread_key_create(&key, destroy);
    
    if (pthread_create(&thread_1, 0, thread_1_func, 0)) {
        printf("thread 1 create failed... \n");
    }
    if (pthread_create(&thread_2, 0, thread_2_func, 0)) {
        printf("thread 2 create failed... \n");
    }
    if (pthread_create(&thread_3, 0, thread_3_func, 0)) {
        printf("thread 3 create failed... \n");
    }
    
    //sleep(5);

    pthread_join(thread_1, 0);
    pthread_join(thread_2, 0);
    pthread_join(thread_3, 0);
    
    pthread_cond_destroy(&cond);
    pthread_mutex_destroy(&mutex);
    pthread_key_delete(key);
    
    printf("exit \n");
    
    return 0;
}
相關文章
相關標籤/搜索