Linux下多線程的實現(基於pthread庫)

Linux內核在2.2版本中引入了類似線程的機制。Linux提供的vfork函數可以創建線程,此外Linux還提供了clone來創建一個線程,通過共享原來調用進程的地址空間,clone能像獨立線程一樣工作。Linux內核的獨特,允許共享地址空間,clone創建的進程指向了父進程的數據結構,從而完成了父子進程共享內存和其他資源。clone的參數可以設置父子進程共享哪些資源,不共享哪些資源。實質上Linux內核並沒有線程這個概念,或者說Linux不區分進程和線程。Linux喜歡稱他們爲任務。除了clone進程以外,Linux並不支持多線程,獨立數據結構或內核子程序。但是POSIX標準提供了Pthread接口來實現用戶級多線程編程。

關於線程更詳細的介紹看這裏:https://blog.csdn.net/zy010101/article/details/83547157

POSIX下開發多線程主要依賴的就是Pthread。使用它需要包含頭文件#include<pthread.h>。因爲這個庫在Pthread之中,在編譯的時候需要加上參數:-lpthread.

線程的創建

pthread_create()函數用於創建一個線程。他的函數原型如下

extern int pthread_create (pthread_t *__restrict __newthread,
			   const pthread_attr_t *__restrict __attr,
			   void *(*__start_routine) (void *),
			   void *__restrict __arg);
  • __newthread是要創建線程的線程ID指針
  • __restrict __attr是創建線程時的屬性
  • void *(*__start_routine) (void *)線程函數的入口地址
  • __arg是傳遞給線程函數的參數

函數返回值:調用成功後返回0,否則,創建線程失敗。從第三個參數,也就是線程函數入口地址。從這兒可以知道線程函數的書寫格式應該是具有void *類型的返回值,另外參數也是void *類型的。

線程創建以後以後的調度仍舊是不確定的。實際上,在Linux下線程ID是使用一個無符號長整型來表示的。

等待線程結束

pthread_join()函數用於等待線程結束,回收資源。類似於進程等待還是waitpid。

函數原型:int pthread_join(thread_t tid,void **status);

函數功能:tid是指定的要等待的線程ID,指定的線程必須位於當前進程之中,而且不能是分離線程。status指向線程退出狀態的指針。

函數返回值:成功返回0,否則表示出現錯誤。

pthread_join只能適用於非分離的線程,因此如果沒有必要等待線程終止,則應該將該線程分離。如果線程已經處於分離狀態,那麼調用失敗。

線程終止

一個線程的終止有3種情況:

  • 線程調用了pthread_exit()函數退出
  • 線程被同一進程的其他線程取消
  • 線程從執行函數返回,返回值是線程退出碼

有一個特殊情形是main所在的線程,我們稱之爲“初始線程”。從mian返回的時候,整個進程都被終止了,因此該進程所有的線程也被終止。還有就是在任意線程內調用exit函數會讓該線程所在的進程整個退出。主動退出線程的時候一定要使用pthread_exit函數,而不是exit。pthread_exit在退出線程以後並不會釋放資源,而是需要pthread_join函數來釋放。當主線程調用這個pthread_exit函數僅僅只是終止主線程,其他線程仍將繼續存在。

函數原型:void pthread_exit(void *retval)

參數retval可以通過pthread_join()來訪問到這個指針。如果線程成功返回到啓動它的線程,那麼retval就會包含返回碼,如果線程被取消,retval就會指向包含內容爲PTHREAD_CANCELED的單元。如果對線程結束的返回值並不感興趣,那麼將retval設置爲NULL即可。

一個測試程序如下:

#include<pthread.h>
#include<stdio.h>
#include<stdlib.h>
#include<errno.h>

void *fun(void *arg)        //線程函數
{
    printf("%s\n",(char *)arg);     //打印傳遞過來的參數
    printf("thread\n");
    pthread_exit(NULL);     //調用pthread_exit函數退出線程
    printf("test!\n");
}
int main()
{
    pthread_t id;
    int t = pthread_create(&id,NULL,fun,"argue");
    if(0 != t)
    {
        perror("pthread_create fail");
        exit(errno);
    }
    printf("main\n");
    pthread_join(id,NULL);      //等待線程執行返回

    return 0;
}

執行結果如下:

在編譯源代碼的時候需要鏈接pthread庫,編譯選項需要加上 -lpthread。

運行結果是正確的,主線程等待子線程結束,在子線程中調用了的pthread_exit函數結束了子線程,所以沒有打印test!。

下面這個例子是對上面的這個例子一點小小的變化,可以通過pthread_join()來獲取pthread_exit()的返回值。

#include<pthread.h>
#include<stdio.h>
#include<stdlib.h>
#include<errno.h>

void *fun(void *arg)                //線程函數
{
    printf("%s\n",(char *)arg);     //打印傳遞過來的參數
    printf("thread\n");
    pthread_exit((void *)3);        //調用pthread_exit函數退出線程,並設置退出碼

    printf("test!\n");              //不會執行到這裏
}
int main()
{
    pthread_t id;
    void *status;

    int t = pthread_create(&id,NULL,fun,"arguement");
    if(0 != t)
    {
        perror("pthread_create fail");
        exit(errno);
    }

    printf("main\n");
    pthread_join(id,&status);      //等待線程執行返回
    printf("%ld\n",(long)status);   //打印線程退出碼

    return 0;
}

線程取消

前面說過線程的終止包含3種情況,其中有一種就是線程被取消了。線程可以通過pthread_cancle來請求取消同一進程內的其他線程。

函數原型:int pthread_cancel(pthread_t thread);

該函數只是去請求取消,而不是命令取消。因此,默認情形下,他會使得線程取消。但是線程可以選擇忽略或者控制如何取消。

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章