帶你學習多線程編程

線程概念

定義

線程就是進程內部的執行流,一個進程至少有一個線程,線程擁有自己的私有資源同時也會和進程共享資源。

線程獨有的資源

  • 線程描述符
  • 寄存器
  • 線程棧
  • errno
  • 信號掩碼
  • 實時調度策略

線程和進程共享的資源

  • 全局變量
  • 代碼段
  • 文件描述符表
  • 進程ID和組ID
  • 每種信號的處理方式
  • 當前工作目錄

    線程和進程的區別

  • 線程是資源調度的最小單位 ,進程時資源分配的最小單位
  • 進程是一次程序運行活動,線程是進程中的一個執行路徑
  • 進程之間不能共享資源,而線程共享所在進程的地址空間和其它資源。同時線程還有自己的棧和棧指針,程序計數器等寄存器。
  • 進程有自己獨立的地址空間,而線程沒有,線程必須依賴於進程而存在。

線程的創建

#include <pthread.h>

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

參數:

  1. thread:返回線程的id
  2. attr:線程屬性,可以手動設置(下文將詳細描述線程屬性)
  3. start_routine:線程執行的函數的函數地址
  4. arg:線程執行函數的參數
    返回值:
    成功返回0,失敗返回錯誤碼。

    線程的終止

    有三種方式終止一個線程。

  5. 用return返回。
  6. 用pthread_exit();
    #include <pthread.h>
    void pthread_exit(void *retval);

    參數:
    retval:保存線程退出碼,<font color="#dd00dd">這個指針一定要是全局變量或者堆上開闢的。</font><br/>

  7. 用pthread_cancel()
    #include <pthread.h>
    int pthread_cancel(pthread_t thread);

    參數:
    thread:結束的線程ID(可以結束任意線程)
    返回值:
    成功返回0,失敗返回錯誤碼。
    線程的終止不能用exit(),這是進程的終止方式。

線程的等待與分離

爲什麼需要線程等待

  • 不等待,線程結束後不會自動釋放資源。
  • 會導致過多線程描述符被佔用,無法創建新的線程。

    線程等待函數

    #include <pthread.h>
    int pthread_join(pthread_t thread, void **retval);

    參數:
    thread:等待線程的ID
    retval:保存退出狀態碼
    返回值:
    成功返回0,失敗返回錯誤碼。

    線程分離

    當我們不關心線程的退出狀態,只希望線程結束系統會自動清理和釋放資源,這時我們就可以使用線程分離。

    #include <pthread.h>
    int pthread_detach(pthread_t thread);

    參數:
    thread:分離的線程ID
    返回值:
    成功返回0,失敗返回錯誤碼。
    線程一旦被pthread_detach()分離,就不能再用pthread_join()獲取其狀態了,也不能再返回鏈接狀態。

    線程屬性

    在前面pthread_create()函數時,就涉及到pthread_attr_t *attr線程屬性,我們可以手動設置線程屬性。

    int pthread_attr_init(pthread_attr_t *attr);//初始化線程屬性
    int pthread_attr_destroy(pthread_attr_t *attr);//銷燬線程屬性對象
    Detach state        = PTHREAD_CREATE_JOINABLE//分離屬性
    int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);//設置分離屬性
    int pthread_attr_getdetachstate(pthread_attr_t *attr, int *detachstate);//獲得分離屬性
    Scope               = PTHREAD_SCOPE_SYSTEM//搶奪資源範圍
    Inherit scheduler   = PTHREAD_INHERIT_SCHED//是否繼承線程調度策略
    Scheduling policy   = SCHED_OTHER//調度策略
    Scheduling priority = 0//調度優先級
    Guard size          = 4096 bytes//線程棧之間的安全區
    Stack address       = 0x40196000//自己指定線程棧
    Stack size          = 0x201000 bytes//棧的大小

    線程屬性這一塊,本文只是簡單列出,更深層次的掌握需要大家自行查閱資料。

    線程的優缺點

    線程的優點

  • 創建一個線程的代價要比創建一個進程的代價小得多。
  • 與進程之間的切換相比較,線程之間的切換操作系統要做的工作少得多。
  • 線程佔用的資源比進程少得多。
  • 能充分利用 多處理器的可並行數量。
  • 在等待慢速I/O操作結束時,程序可執行其他的計算任務。
  • 計算密集型應用,多處理器運行,可以將計算分佈到多個線程中計算。
  • I/O密集型應用,爲了提高性能,將I/O操作重疊,線程可以等待不同的I/O操作。

    線程的缺點

  • 性能損失
  • 健壯性降低。
  • 缺乏訪問控制
  • 編程難度提高
  • 多線程對GDB支持不好
  • 多線程對信號支持不好

    線程互斥

    互斥量

    互斥量

    1. 定義互斥鎖:pthread_mutex_t mutex;
    2. 初始化:pthread_mutex_init(&mutex,NULL);
    3. 上鎖:pthread_mutex_lock(&mutex);
    4. 解鎖:pthread_mutex_unlock(&mutex);
    5. 銷燬:pthread_mutex_destroy(&mutex);

      自旋鎖

    6. 定義自旋鎖:pthread_spinlock_t spin;
    7. 初始化:int pthread_spin_init(pthread_spinlock_t *lock, int pshared);
    8. 上鎖:int pthread_spin_lock(pthread_spinlock_t *lock);
    9. 解鎖:int pthread_spin_unock(pthread_spinlock_t *lock);
    10. 銷燬鎖:int pthread_spin_destroy(pthread_spinlock_t *lock);

自旋鎖與互斥鎖的區別

自旋鎖和互斥所的區別:互斥鎖是當阻在pthread_mutex_lock時,放棄CPU,好讓別人使用CPU。自旋鎖阻塞在spin_lock時,不會釋放CPU,不斷的問CPU可以使用了不

讀寫鎖

  1. pthread_rwlock_t lock;
  2. 初始化:int pthread_rwlock_init(pthread_rwlock_t restrict rwlock,const pthread_rwlockattr_t restrict attr);
  3. 讀鎖:int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
  4. 寫鎖:int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
  5. 解鎖:int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);
  6. 銷燬鎖:int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);

讀讀共享,讀寫排他,寫寫排他,寫鎖優先.

同步

條件變量

  1. 定義條件變量:pthread_cond_t cond;
  2. 初始化條件變量:int pthread_cond_init(pthread_cond_t restrict cond,const pthread_condattr_t restrict attr);
  3. 等待條件:int pthread_cond_wait(pthread_cond_t restrict cond,pthread_mutex_t restrict mutex);
        如果沒有在鎖環境下使用,互斥量形同虛設
        如果在鎖環境下,將mutex解鎖
        wait返回時,將mutex置爲原來的狀態
  4. 使條件滿足:int pthread_cond_signal(pthread_cond_t *cond);
  5. 銷燬條件變量:int pthread_cond_destroy(pthread_cond_t *cond);

綜合案例

pthread_mutex_t mutex;//創建互斥量
int a = 0;
int b = 0;
void *r1(void* arg) //線程1執行函數
{
    while(1)
    {
        pthread_mutex_lock(&mutex);//上鎖
        a++;
        b++;
        if(a != b)
        {
            printf("%d != %d\n",a,b);
        }
        pthread_mutex_unlock(&mutex);//解鎖
    }
}

void *r2(void* arg)//線程2執行函數
{
    while(1)
    {
        pthread_mutex_lock(&mutex);
        a++;
        b++;
        if(a != b)
        {
            printf("%d != %d\n",a,b);
        }
        pthread_mutex_unlock(&mutex);
    }   
}
int main(void)
{
    pthread_t t1,t2;
    pthread_mutex_init(&mutex,NULL);//初始化互斥量
    pthread_create(&t1,NULL,r1,NULL);//創建線程
    pthread_create(&t2,NULL,r2,NULL);//創建線程

    pthread_join(t1,NULL);//線程等待
    pthread_join(t2,NULL);
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章