線程與線程同步機制


線程

一、概念:

  線程就是程序的執行路線,即進程內部的控制序列,或者說是進程的子任務。
  線程,輕量級,不擁有自己獨立的內存資源,共享進程的代碼區、數據區、堆區(注意沒有棧區)、環境變量和命令行參數、文件描述符、信號處理函數、當前目錄、用戶ID和組ID等資源。
  線程擁有自己獨立的棧,因此也有自己獨立的局部變量。
  一個進程可以同時擁有多個線程,即同時被系統調度的多條執行路線,但至少要有一個主線程。

二、線程函數

  1.創建線程

    int pthread_create (pthread_t* restrict thread,
    const pthread_attr_t* restrict attr,
    void* (*start_routine) (void*),
    void* restrict arg);

    thread-     線程ID,輸出參數。
            pthread_t即unsigned long int。

    attr -        線程屬性,NULL表示缺省屬性。
            pthread_attr_t可能是整型也可能是結構,
            因實現而異。

    start_routine-     線程過程函數指針,
            參數和返回值的類型都是void*。
            啟動線程本質上就是調用一個函數,
            只不過是在一個獨立的線程中調用的,
            函數返回即線程結束。

    arg -        傳遞給線程過程函數的參數。
            線程過程函數的調用者是系統內核,
            而非用戶代碼,
            因此需要在創建線程時指定參數。

  2.等待線程

    int pthread_join (pthread_t thread, void** retval);
    等待thread參數所標識的線程結束,成功返回0,失敗返回錯誤碼。

  3.獲取線程自身的ID

    pthread_t  pthread_self (void);
    成功返回調用線程的ID,不會失敗。

  4.比較兩個線程的ID

    int pthread_equal (pthread_t t1, pthread_t t2);
    若參數t1和t2所標識的線程ID相等,則返回非零,否則返回0。

  5.終止線程

    ① 從線程過程函數中return。

    ② 調用pthread_exit函數。

    void pthread_exit (void* retval);

    retval -        和線程過程函數的返回值語義相同。

    注意:在任何線程中調用exit函數都將終止整個進程。

  6.線程執行軌跡

    ① 同步方式(非分離狀態):
     創建線程之后調用pthread_join函數等待其終止,並釋放線程資源。
    ② 異步方式(分離狀態):
     無需創建者等待,線程終止后自行釋放資源。

    int pthread_detach (pthread_t thread);
    使thread參數所標識的線程進入分離(DETACHED)狀態。
    處於分離狀態的線程終止后自動釋放線程資源,且不能被pthread_join函數等待。
    成功返回0,失敗返回錯誤碼。

  7.取消線程

    ① 向指定線程發送取消請求
      int pthread_cancel (pthread_t thread);
      成功返回0,失敗返回錯誤碼。
    注意:該函數只是向線程發出取消請求,並不等待線程終止。
      缺省情況下,線程在收到取消請求以后,並不會立即終止,而是仍繼續運行,直到其達到某個取消點。
      在取消點處,線程檢查其自身是否已被取消了,並做出相應動作。
      當線程調用一些特定函數時,取消點會出現。

    ② 設置調用線程的可取消狀態
      int pthread_setcancelstate (int state,int* oldstate);
      成功返回0,並通過oldstate參數輸出原可取消狀態(若非NULL),失敗返回錯誤碼。
      state取值:
      PTHREAD_CANCEL_ENABLE - 接受取消請求(缺省)。
      PTHREAD_CANCEL_DISABLE - 忽略取消請求。

    ③ 設置調用線程的可取消類型
      int pthread_setcanceltype (int type, int* oldtype);
      成功返回0,並通過oldtype參數輸出原可取消類型,(若非NULL),失敗返回錯誤碼。
      type取值:
      PTHREAD_CANCEL_DEFERRED - 延遲取消(缺省)。
        被取消線程在接收到取消請求之后並不立即響應,
        而是一直等到執行了特定的函數(取消點)之后再響應該請求。
      PTHREAD_CANCEL_ASYNCHRONOUS - 異步取消。
        被取消線程可以在任意時間取消,不是非得遇到取消點才能被取消。
        但是操作系統並不能保證這一點。

 線程同步

  一、互斥鎖(mutex)    

    通過鎖機制實現線程間的同步。同一時刻只允許一個線程執行一個關鍵部分的代碼。

1 int pthread_mutex_init(pthread_mutex_t *mutex,const pthread_mutex_attr_t *mutexattr);
2 int pthread_mutex_lock(pthread_mutex_t* mutex); 
3 int pthread_mutex_destroy(pthread_mutex_t * mutex); 
4 int pthread_mutex_unlock(pthread_mutex_t * mutex);

    ① 互斥量被初始化為非鎖定狀態

    ② 線程1調用pthread_mutex_lock函數,立即返回,互斥量呈鎖定狀態;

    ③ 線程2調用pthread_mutex_lock函數,阻塞等待;

    ④ 線程1調用pthread_mutex_unlock函數,互斥量呈非鎖定狀態; 

    ⑤ 線程2被喚醒,從pthread_mutex_lock函數中返回,互斥量呈鎖定狀態

    特點:
      互斥鎖是用在多線程多任務互斥的,一個線程占用了某一個資源,那么別的線程就無法訪問,

      直到這個線程unlock,其他的線程才開始可以利用這 個資源。

互斥鎖類型:
① 普通鎖 (PTHREAD_MUTEX_NORMAL)
      互斥鎖默認類型。當一個線程對一個普通鎖加鎖以后,其余請求該鎖的線程將形成一個等待隊列,並在該鎖解鎖后按照優先級獲得它,這種鎖類型保證了資源分配的公平性。一個線程如果對一個已經加鎖的普通鎖再次加鎖,將引發死鎖;對一個已經被其他線程加鎖的普通鎖解鎖,或者對一個已經解鎖的普通鎖再次解鎖,將導致不可預期的后果。
② 檢錯鎖(PTHREAD_MUTEX_ERRORCHECK)
     一個線程如果對一個已經加鎖的檢錯鎖再次加鎖,則加鎖操作返回EDEADLK;對一個已經被其他線程加鎖的檢錯鎖解鎖或者對一個已經解鎖的檢錯鎖再次解鎖,則解鎖操作返回EPERM;
          ③ 嵌套鎖(PTHREAD_MUTEX_RECURSIVE)
     該鎖允許一個線程在釋放鎖之前多次對它加鎖而不發生死鎖;其他線程要獲得這個鎖,則當前鎖的擁有者必須執行多次解鎖操作;對一個已經被其他線程加鎖的嵌套鎖解鎖,或者對一個已經解鎖的嵌套鎖再次解鎖,則解鎖操作返回EPERM
④ 默認鎖(PTHREAD_MUTEX_ DEFAULT)
     一個線程如果對一個已經加鎖的默認鎖再次加鎖,或者雖一個已經被其他線程加鎖的默認鎖解鎖,或者對一個解鎖的默認鎖解鎖,將導致不可預期的后果;這種鎖實現的時候可能被映射成上述三種鎖之一;

  二、信號量(sem)

    如同進程一樣,線程也可以通過信號量來實現通信,雖然是輕量級的。

    信號量是一個計數器,用於控制訪問有限共享資源的線程數。    

 1 // 創建信號量
 2 int sem_init (sem_t* sem, int pshared,unsigned int value);
 3 sem     - 信號量ID,輸出。
 4 pshared - 一般取0,表示調用進程的信號量。
 5           非0表示該信號量可以共享內存的方式,
 6           為多個進程所共享(Linux暫不支持)。
 7 value   - 信號量初值。
 8 
 9 // 信號量減1,不夠減即阻塞
10 int sem_wait (sem_t* sem);
11 
12 // 信號量減1,不夠減即返回-1,errno為EAGAIN
13 int sem_trywait (sem_t* sem);
14 
15 // 信號量減1,不夠減即阻塞,
16 // 直到abs_timeout超時返回-1,errno為ETIMEDOUT
17 int sem_timedwait (sem_t* sem,
18 const struct timespec* abs_timeout);
19 
20 struct timespec {
21     time_t tv_sec;  // Seconds
22     long   tv_nsec; // Nanoseconds [0 - 999999999]
23 };
24 
25 // 信號量加1
26 int sem_post (sem_t* sem);
27 
28 // 銷毀信號量
29 int sem_destroy (sem_t* sem);

    特點:
    信號量用在多線程多任務同步的,一個線程完成了某一個動作就通過信號量告訴別的線程,別的線程再進行某些動作
   (大家都在semtake的時候,就阻塞在哪里)

   三、條件變量(cond)

    條件變量可以讓調用線程在滿足特定條件的情況下暫停。

int pthread_cond_init (pthread_cond_t* cond,const pthread_condattr_t* attr);
//亦可pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

// 使調用線程睡入條件變量cond,同時釋放互斥鎖mutex
int pthread_cond_wait (pthread_cond_t* cond,pthread_mutex_t* mutex);

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

struct timespec {
    time_t tv_sec;  // Seconds
    long   tv_nsec; // Nanoseconds [0 - 999999999]
};

// 從條件變量cond中喚出一個線程,
// 令其重新獲得原先的互斥鎖
int pthread_cond_signal (pthread_cond_t* cond);

注意:被喚出的線程此刻將從pthread_cond_wait函數中返回,
但如果該線程無法獲得原先的鎖,則會繼續阻塞在加鎖上。

// 從條件變量cond中喚出所有線程
int pthread_cond_broadcast (pthread_cond_t* cond);

int pthread_cond_destroy (pthread_cond_t* cond);

   四、

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM