首頁  >  文章  >  運維  >  深入了解Linux系統程式設計-(pthread)執行緒建立與使用

深入了解Linux系統程式設計-(pthread)執行緒建立與使用

WBOY
WBOY轉載
2022-02-02 07:00:312468瀏覽

這篇文章為大家帶來了關於linux中線程的創建與使用相關知識,希望對大家有幫助。

深入了解Linux系統程式設計-(pthread)執行緒建立與使用

1.前言

執行緒與進程的差異 (1)進程: 是作業系統調度最小單位。 Linux下可以透過ps、top等指令查看進程的詳細資料。 (2)執行緒: 是進程調度的最小單位,每個行程都有一個主執行緒。在行程裡主要做事情就是執行緒。

(3)在全系統中,進程ID是唯一標識,對於進程的管理都是透過PID來實現的。每建立一個進程,核心去中就會創建一個結構體來儲存該進程的全部訊息,每一個儲存進程資訊的節點也都保存著自己的PID。需要管理該行程時就透過這個ID來實現(例如發送訊號)。當子進程結束要回收時(子進程呼叫exit()退出或程式碼執行完),需要透過wait()系統呼叫來進行,未回收的消亡進程會成為殭屍進程,其進程實體已經不復存在,但會虛佔PID資源,因此回收是必要的。

對於執行緒而言,若要主動終止需要呼叫pthread_exit() ,主執行緒需要呼叫pthread_join()來回收(前提是該執行緒沒有設定 「分離屬性」)。像線發送線程訊號也是透過線程ID實現

進程間的通訊方式: A.共享記憶體B.訊息佇列C.信號量D.有名管道E.無名管道F.訊號G.檔案H. socket 執行緒間的通訊方式: A.互斥量B.自旋鎖C.條件變數D.讀寫鎖E.執行緒訊號F.全域變數

進程間採用的通訊方式要麼需要切換內核上下文,要么要與外設訪問(有名管道,文件)。所以速度會比較慢。而線程採用自己特有的通信方式的話,基本上都在自己的進程空間內完成,不存在切換,所以通信速度會較快。也就是說,進程間與執行緒間分別採用的通訊方式,除了種類的差異外,還有速度上的差異。

說明: 當執行多執行緒的程序捕獲到訊號時,只會阻塞主線程,其他子執行緒不會影響會繼續執行。

2. 執行緒相關函數介紹

2.1 建立執行緒

pthread_create是Unix作業系統(Unix、Linux等)的建立執行緒的函數。編譯時需要指定連結函式庫: -lpthread 函式原型

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

參數介紹

第一個參數為指向執行緒標識符的指標。第二個參數用來設定線程屬性。預設可填NULL。第三個參數是執行緒運行函數的起始位址。最後一個參數是運行函數的參數。不需要參數可填NULL。 Linux下查看函數幫助:# man pthread_create

深入了解Linux系統程式設計-(pthread)執行緒建立與使用

傳回值: 若執行緒建立成功,則傳回0。若執行緒建立失敗,則傳回出錯編號。當執行緒建立成功後, attr參數用於指定各種不同的執行緒屬性。新建立的執行緒從start_rtn函數的位址開始運行,該函數只有一個萬能指標參數arg,如果需要向執行緒工作函數傳遞的參數不只一個,那麼需要把這些參數放到一個結構中,然後把這個結構的位址作為arg的參數傳入。

範例:

#include 
#include 
//线程函数1
void *pthread_func1(void *arg)
{
    while(1)
    {
        printf("线程函数1正在运行.....\n");
        sleep(2);
    }
}
//线程函数2
void *pthread_func2(void *arg)
{
    while(1)
    {
        printf("线程函数2正在运行.....\n");
        sleep(2);
    }
}
int main(int argc,char **argv)
{
    
    pthread_t thread_id1;
    pthread_t thread_id2;
   /*1. 创建线程1*/
    if(pthread_create(&thread_id1,NULL,pthread_func1,NULL))
    {
        printf("线程1创建失败!\n");
        return -1;
    }
    /*2. 创建线程2*/
    if(pthread_create(&thread_id2,NULL,pthread_func2,NULL))
    {
        printf("线程2创建失败!\n");
        return -1;
    }
    
    /*3. 等待线程结束,释放线程的资源*/
    pthread_join(thread_id1,NULL);
    pthread_join(thread_id2,NULL);
    return 0;
}
//gcc pthread_demo_code.c -lpthread

2.2 退出執行緒

執行緒透過呼叫pthread_exit函數終止執行,就如同進程在結束時呼叫exit函數一樣。這個函數的作用是,終止呼叫它的執行緒並傳回一個指向某個物件的指標。

這個函數的作用是,終止呼叫它的線程並傳回一個指向某個物件的指針,該回傳值可以透過pthread_join函數的第二個參數來得到。

函數原型

#include 
void pthread_exit(void *retval);

參數解析 執行緒的需要傳回的位址。注意: 執行緒結束必須釋放執行緒堆疊,就是說執行緒函數必須呼叫pthread_exit()結束,否則直到主行程函數退出才釋放

2.3 等待執行緒結束

# pthread_join()函數,以阻塞的方式等待thread指定的執行緒結束。當函數返回時,被等待線程的資源被收回。如果線程已經結束,那麼函數會立即傳回。且thread指定的執行緒必須是joinable(結合屬性)屬性。函數原型

#include 
int pthread_join(pthread_t thread, void **retval);

參數 第一個參數: 執行緒標識符,即執行緒ID,標識唯一執行緒。最後一個參數: 使用者定義的指針,用來儲存被等待線程返回的位址。傳回值 0代表成功。失敗,回傳的則是錯誤號碼。接收執行緒傳回值範例:

//退出线程
pthread_exit ("线程已正常退出");
//接收线程的返回值
void *pth_join_ret1;
pthread_join( thread1, &pth_join_ret1);

2.4 執行緒分離屬性

#

创建一个线程默认的状态是joinable(结合属性),如果一个线程结束运行但没有调用pthread_join,则它的状态类似于进程中的Zombie Process(僵死进程),即还有一部分资源没有被回收(退出状态码),所以创建线程者应该pthread_join来等待线程运行结束,并可得到线程的退出代码,回收其资源(类似于进程的wait,waitpid)。但是调用pthread_join(pthread_id)函数后,如果该线程没有运行结束,调用者会被阻塞,在有些情况下我们并不希望如此。

pthread_detach函数可以将该线程的状态设置为detached(分离状态),则该线程运行结束后会自动释放所有资源。 函数原型

#include 
int pthread_detach(pthread_t thread);

参数 线程标识符 返回值 0表示成功。错误返回错误码。 EINVAL线程并不是一个可接合线程。 ESRCH没有线程ID可以被发现。

2.5 获取当前线程的标识符

pthread_self函数功能是获得线程自身的ID。 函数原型

#include 
pthread_t pthread_self(void);

返回值 当前线程的标识符。 pthread_t的类型为unsigned long int,所以在打印的时候要使用%lu方式,否则显示结果出问题。

2.6 自动清理线程资源

线程可以安排它退出时需要调用的函数,这样的函数称为线程清理处理程序。用于程序异常退出的时候做一些善后的资源清理。 在POSIX线程API中提供了一个pthread_cleanup_push()/pthread_cleanup_pop()函数用于自动释放资源。从pthread_cleanup_push()的调用点到pthread_cleanup_pop()之间的程序段中的终止动作(包括调用 pthread_exit()和异常终止)都将执行pthread_cleanup_push()所指定的清理函数。

注意:pthread_cleanup_push函数与pthread_cleanup_pop函数需要成对调用。 函数原型

void pthread_cleanup_push(void (*routine)(void *),void *arg); //注册清理函数
void pthread_cleanup_pop(int execute); //释放清理函数

参数 void (*routine)(void *) :处理程序的函数入口。 void *arg :传递给处理函数的形参。 int execute:执行的状态值。 0表示不调用清理函数。1表示调用清理函数。

导致清理函数调用的条件:

调用pthread_exit()函数

pthread_cleanup_pop的形参为1。 注意:return不会导致清理函数调用。

2.7 自动清理线程示例代码

#include 
#include 
#include 
//线程清理函数
void routine_func(void *arg)
{
   printf("线程资源清理成功\n");
}
//线程工作函数
void *start_routine(void *dev)
{
   pthread_cleanup_push(routine_func,NULL);
   //终止线程
   // pthread_exit(NULL);
    
   pthread_cleanup_pop(1); //1会导致清理函数被调用。0不会调用。
}
int main(int argc,char *argv[])
{
   pthread_t thread_id;  //存放线程的标识符
   /*1. 创建线程*/
   if(pthread_create(&thread_id,NULL,start_routine,NULL)!=0)
   {
      printf("线程创建失败!\n");
   } 
  /*2.设置线程的分离属性*/
   if(pthread_detach(thread_id)!=0)
   {
   printf("分离属性设置失败!\n");
   }
   while(1){}
   return 0;
}

2.8 线程取消函数

pthread_cancel函数为线程取消函数,用来取消同一进程中的其他线程。

头文件: #include 
函数原型:pthread_cancel(pthread_t tid);

相关推荐:《Linux视频教程

以上是深入了解Linux系統程式設計-(pthread)執行緒建立與使用的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:juejin.im。如有侵權,請聯絡admin@php.cn刪除