Linux多線程編程實例解析

      網友投稿 948 2022-05-29

      1.背景知識

      linux沒有真正意義上的線程,它的實現是由進程來模擬,所以屬于用戶級線程,位于libpthread共享庫(所以線程的ID只在庫中有效),遵循POSIX標準。

      Windows下有一個真正的數據結構TCB來描述線程。

      Linux上兩個最有名的線程庫LinuxThreads和NPTL。

      Linux兩個線程模型的比較:

      Linux線程模型的比較

      Linux下多線程虛擬地址空間的映射類似于用vfork創建多個子進程。

      2.進程和線程的區別

      進程:程序的一個動態運行實例,承擔分配系統資源的實例。(Linux實現進程的主要目的是資源獨占)

      線程:在進程的內部運行(進程的地址空間)運行的一個分支,也是調度的基本單位(調度按LWP調度)。(Linux實現線程的主要目的是資源共享)

      線程所有的資源由進程提供。

      單進程:只有一個進程的線程(LWP=PID)。

      LWP:輕量級進程。

      由于同一進程的多個線程共享同一地址空間,因 此Text Segment、Data Segment都是共享的,如果定義一個函數,在各線程中都可以調用,如果定義一個全局變量,在各線程中都可以訪問到,除此之外,各線程還共享以下進程資源和環境:

      1. 文件描述符表

      2. 每種信號的處理方式(SIG_IGN、SIG_DFL或者自定義的信號處理函數)

      3. 當前工作目錄

      4. 用戶id和組id

      但有些資源是每個線程各有一份的:

      1.線程ID

      2. 上下文信息,包括各種寄存器的值、程序計數器和棧指針

      3. 棧空間

      4. errno變量

      5. 信號屏蔽字

      6. 調度優先級

      多線程程序的優點(相對進程比較而言):

      1.?多個線程,它們彼此之間使用相同的地址空間,共享大部分數據,啟動一個線程所花費的空間遠遠小于啟動一個進程所花費的空間,而且,線程間彼此切換所需的時間也遠遠小于進程間切換所需要的時間,創建銷毀速度快。

      2.是線程間方便的通信機制。由于同一進程下的線程之間共享數據空間,所以一個線程的數據可以直接為其它線程所用,這不僅快捷,而且方便。

      3.進程控制

      在Linux系統下,與線程相關的函數都定義在pthread.h頭文件中。

      創建線程函數———pthread_create函數

      #include

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

      (1)thread參數是新線程的標識符,為一個整型。

      (2)attr參數用于設置新線程的屬性。給傳遞NULL表示設置為默認線程屬性。

      (3)start_routine和arg參數分別指定新線程將運行的函數和參數。start_routine返回時,這個線程就退出了

      (4)返回值:成功返回0,失敗返回錯誤號。

      線程id的類型是thread_t,它只在當前進程中保證是唯一的,在不同的系統中thread_t這個類型有不同的實現,調用pthread_self()可以獲得當前線程的id

      進程id的類型時pid_t,每個進程的id在整個系統中是唯一的,調用getpid()可以獲得當前進程的id,是一個正整數值。

      終止線程———pthread_cancel函數和pthread_exit函數

      終止某個線程而不終止整個進程,可以有三種方法:

      1. 從線程函數return。這種方法對主線程不適應,從main函數return相當于調用exit。

      2. 一個線程可以調用pthread_cancel終止同一進程中的另一個線程。

      3. 線程可以調用pthread_exit終止自己。

      #include

      int pthread_cancel(pthread_t thread);

      (1)thread參數是目標線程的標識符。

      (2)該函數成功返回0,失敗返回錯誤碼。

      #include

      void pthread_exit(void * retval);

      (1)retval是void *類型,其它線程可以調用pthread_join獲得這個指針。需要注意,pthread_exit或者return返回的指針所指向的內存單元必須是全局的或者是由malloc分 配的,不能在線程函數的棧上分配,因為當其它線程得到這個返回指針時線程函數已經退出了。

      (2)pthread_exit函數通過retval參數向線程的回收者傳遞其退出信息。它執行之后不會返回到調用者,且永遠不會失敗。

      線程等待———pthread_join

      #include

      void pthread_join(pthread_t thread,void ** retval);

      (1)調用該函數的線程將掛起等待,直到id為thread的線程終止。

      (2)thread線程以不同的方法終止,通過pthread_join得到的終止狀態是不同的,

      總結如下:

      1. 如果thread線程通過return返回,value_ptr所指向的單元里存放的是thread線程函數的返回值。

      2. 如果thread線程被別的線程調用pthread_cancel異常終掉,value_ptr所指向的單元里存放的是常數PTHREAD_CANCELED。

      3. 如果thread線程是自己調用pthread_exit終止的,value_ptr所指向的單元存放的是傳給pthread_exit的參數。 如果對thread線程的終止狀態不感興趣,可以傳NULL給value_ptr參數。

      (3)成功返回0,失敗返回錯誤碼。可能出現的錯誤碼:

      4.分離線程

      1.在任何一個時間點上,線程是可結合的(joinable)或者是分離的(detached)。

      2.一個可結合的線程能夠被其他線程收回其資源和殺死。在被其他線程回收之前,它的存儲器資源

      (例如棧)是不釋放的。(默認情況下線程的創建都是可結合的)

      3.一個分離的線程是不能被其他線程回收或殺死的,它的存儲器?資源在它終止時由系統自動釋放。

      4. 如果一個可結合線程結束運行但沒有被join,會導致部分資源沒有被回收,所以創建線程者應該調用pthread_join來等待線程運行結束,并可得到線程的退出代碼,回收其資源。

      調用pthread_join后,如果該線程沒有運行結束,調用者會被阻塞。如何解決這種情況呢?

      例如,在Web服務器中當主線程為每個新來的連接請求創建一個子線程進行處理的時候,主線程并不希望因為調用pthread_join而阻塞(因為還要繼續處理之后到來的連接請求),這時可以在子線程中加入代碼 pthread_detach(pthread_self())或者父線程調用pthread_detach(thread_id)(非阻塞,可立即返回)這將該子線程的狀態設置為分離的(detached),如此一來,該線程運行結束后會自動釋放所有資源。

      Linux系統下的多線程遵循POSIX線程接口,稱為 pthread。編寫Linux下的多線程程序,需要使用頭文件pthread.h,連接時需要使用庫libpthread.a。順便說一下,Linux 下pthread的實現是通過系統調用clone()來實現的。clone()是 Linux所特有的系統調用,它的使用方式類似fork,關于clone()的詳細情況,有興趣的讀者可以去查看有關文檔說明。下面我們展示一個最簡單的 多線程程序 pthread_create.c。

      一個重要的線程創建函數原型:

      #include

      int pthread_create(pthread_t *restrict tidp,const pthread_attr_t *restrict attr, void *(*start_rtn)(void),void *restrict arg);

      返回值:若是成功建立線程返回0,否則返回錯誤的編號

      形式參數:

      pthread_t *restrict tidp 要創建的線程的線程id指針

      const pthread_attr_t *restrict attr 創建線程時的線程屬性

      void* (start_rtn)(void) 返回值是void類型的指針函數

      void *restrict arg?? start_rtn的行參

      例程1:

      功能:創建一個簡單的線程

      程序名稱:pthread_create.c

      代碼如下:

      #include

      #include

      void *mythread1(void)

      {

      int i;

      for(i = 0; i < 10; i++)

      {

      printf("This is the 1st pthread,created by xiaoqiang!\n");

      sleep(1);

      }

      }

      void *mythread2(void)

      {

      int i;

      for(i = 0; i < 10; i++)

      {

      printf("This is the 2st pthread,created by xiaoqiang!\n");

      sleep(1);

      }

      }

      int main(int argc, const char *argv[])

      {

      int i = 0;

      int ret = 0;

      pthread_t id1,id2;

      ret = pthread_create(&id1, NULL, (void *)mythread1,NULL);

      if(ret)

      {

      printf("Create pthread error!\n");

      return 1;

      }

      ret = pthread_create(&id2, NULL, (void *)mythread2,NULL);

      if(ret)

      {

      printf("Create pthread error!\n");

      return 1;

      }

      pthread_join(id1,NULL);

      pthread_join(id2,NULL);

      return 0;

      }

      執行結果如下:

      fs@ubuntu:~/qiang/thread$ vi thread1.c

      fs@ubuntu:~/qiang/thread$ gcc -o thread1 thread1.c -lpthread

      fs@ubuntu:~/qiang/thread$ ./thread1

      This is the 2st pthread,created by xiaoqiang!

      This is the 1st pthread,created by xiaoqiang!

      This is the 2st pthread,created by xiaoqiang!

      This is the 1st pthread,created by xiaoqiang!

      This is the 2st pthread,created by xiaoqiang!

      This is the 1st pthread,created by xiaoqiang!

      This is the 2st pthread,created by xiaoqiang!

      This is the 1st pthread,created by xiaoqiang!

      This is the 2st pthread,created by xiaoqiang!

      This is the 1st pthread,created by xiaoqiang!

      This is the 2st pthread,created by xiaoqiang!

      This is the 1st pthread,created by xiaoqiang!

      This is the 1st pthread,created by xiaoqiang!

      This is the 2st pthread,created by xiaoqiang!

      This is the 2st pthread,created by xiaoqiang!

      This is the 1st pthread,created by xiaoqiang!

      This is the 1st pthread,created by xiaoqiang!

      This is the 2st pthread,created by xiaoqiang!

      This is the 2st pthread,created by xiaoqiang!

      This is the 1st pthread,created by xiaoqiang!

      fs@ubuntu:~/qiang/thread$

      兩個線程交替執行。

      另外,因為pthread的庫不是linux系統的庫,所以在進行編譯的時候要加上-lpthread,否則編譯不過,會出現下面錯誤

      thread_test.c: 在函數 ‘create’ 中:

      thread_test.c:7: 警告: 在有返回值的函數中,程序流程到達函數尾

      /tmp/ccOBJmuD.o: In function `main':thread_test.c:(.text+0x4f):對‘pthread_create’未定義的引用

      collect2: ld 返回 1

      此例子介紹了創建線程的方法

      下面例子介紹向線程傳遞參數。

      例程2:

      功能:向新的線程傳遞整形值

      程序名稱:pthread_int.c

      代碼如下:

      #include

      #include

      void *create(void *arg)

      {

      int *num;

      num = (int *)arg;

      printf("Create parameter is %d\n",*num);

      return (void *)0;

      }

      int main(int argc, const char *argv[])

      {

      pthread_t id1;

      int error;

      int test = 4;

      int *attr = &test;

      error = pthread_create(&id1,NULL,create,(void *)attr);

      if(error)

      {

      printf("Pthread_create is not created!\n");

      return -1;

      }

      sleep(1);

      printf("Pthread_create is created..\n");

      return 0;

      }

      執行結果如下:

      fs@ubuntu:~/qiang/thread$ vi thread2.c

      fs@ubuntu:~/qiang/thread$ gcc -o thread2 thread2.c -lpthread

      fs@ubuntu:~/qiang/thread$ ./thread2

      Create parameter is 4

      Pthread_create is created..

      fs@ubuntu:~/qiang/thread$

      例程總結:

      可以看出來,我們在main函數中傳遞的整行指針,傳遞到我們新建的線程函數中。

      在上面的例子可以看出來我們向新的線程傳入了另一個線程的int數據,線程之間還可以傳遞字符串或是更復雜的數據結構。

      例程3:

      程序功能:向新建的線程傳遞字符串

      程序名稱:pthread_string.c

      代碼如下:

      #include

      #include

      void *create(char *arg)

      {

      char *str;

      str = arg;

      printf("The parameter passed from main is %s\n",str);

      return (void *)0;

      }

      int main()

      {

      int error;

      pthread_t id1;

      char *str1 = "Hello ,xiaoqiang!";

      char *attr = str1;

      error = pthread_create(&id1, NULL, create, (void *)attr);

      if(error != 0)

      {

      printf("This pthread is not created!\n");

      return -1;

      }

      sleep(1);

      printf("pthread is created..\n");

      return 0;

      }

      執行結果如下:

      fs@ubuntu:~/qiang/thread$ ./thread3

      The parameter passed from main is Hello ,xiaoqiang!

      pthread is created..

      fs@ubuntu:~/qiang/thread$

      例程總結:

      可以看出來main函數中的字符串傳入了新建的線程中。

      例程4:

      程序功能:向新建的線程傳遞字符串

      程序名稱:pthread_struct.c

      代碼如下:

      #include

      #include

      #include

      struct menber

      {

      int a;

      char *s;

      };

      void *create(void *arg)

      {

      struct menber *temp;

      temp = (struct menber *)arg;

      printf("menber->a = %d\n",temp->a);

      printf("menber->s = %s\n",temp->s);

      return (void *)0;

      }

      int main()

      {

      int error;

      pthread_t id1;

      struct menber *p;

      p = (struct menber *)malloc(sizeof(struct menber));

      p->a = 1;

      Linux多線程編程實例解析

      p->s = "xiaoqiang!";

      error = pthread_create(&id1,NULL,create,(void *)p);

      if(error)

      {

      printf("pthread is not created!\n");

      return -1;

      }

      sleep(1);

      printf("pthread is created!\n");

      free(p);

      p = NULL;

      return 0;

      }

      執行結果如下:

      fs@ubuntu:~/qiang/thread$ vi thread4.c

      fs@ubuntu:~/qiang/thread$ gcc -o thread4 thread4.c -lpthread

      fs@ubuntu:~/qiang/thread$ ./thread4

      menber->a = 1

      menber->s = xiaoqiang!

      pthread is created!

      fs@ubuntu:~/qiang/thread$

      例程總結:

      可以看出來main函數中的一個結構體傳入了新建的線程中。

      線程包含了標識進程內執行環境必須的信息。他集成了進程中的所有信息都是對線程進行共享的,包括文本程序、程序的全局內存和堆內存、棧以及文件描述符

      例程5:

      程序目的:驗證新建立的線程可以共享進程中的數據

      程序名稱:pthread_share.c

      代碼如下:

      #include

      #include

      static int a = 5;

      void *create(void *arg)

      {

      printf("New pthread...\n");

      printf("a = %d\n",a);

      return (void *)0;

      }

      int main(int argc, const char *argv[])

      {

      int error;

      pthread_t id1;

      error = pthread_create(&id1, NULL, create, NULL);

      if(error != 0)

      {

      printf("new thread is not created!\n");

      return -1;

      }

      sleep(1);

      printf("New thread is created...\n");

      return 0;

      }

      結果如下:

      fs@ubuntu:~/qiang/thread$ vi thread5.c

      fs@ubuntu:~/qiang/thread$ gcc -o thread5 thread5.c -lpthread

      fs@ubuntu:~/qiang/thread$ ./thread5

      New pthread...

      a = 5

      New thread is created...

      fs@ubuntu:~/qiang/thread$

      例程總結:

      可以看出來,我們在主線程更改了我們的全局變量a的值的時候,我們新建立的線程則打印出來了改變的值,可以看出可以訪問線程所在進程中的數據信息。

      2、線程的終止

      如果進程中任何一個線程中調用exit,_Exit,或者是_exit,那么整個進程就會終止,

      與此類似,如果信號的默認的動作是終止進程,那么,把該信號發送到線程會終止進程。

      線程的正常退出的方式:

      (1) 線程只是從啟動例程中返回,返回值是線程中的退出碼

      (2) 線程可以被另一個進程進行終止

      (3) 線程自己調用pthread_exit函數

      兩個重要的函數原型:

      include

      void pthread_exit(void *rval_ptr);

      /*rval_ptr 線程退出返回的指針*/

      int pthread_join(pthread_t thread,void **rval_ptr);

      /*成功結束進程為0,否則為錯誤編碼*/

      pthread_join使一個線程等待另一個線程結束。

      代碼中如果沒有pthread_join主線程會很快結束從而使整個進程結束,從而使創建的線程沒有機會開始執行就結束了。加入pthread_join后,主線程會一直等待直到等待的線程結束自己才結束,使創建的線程有機會執行。

      頭文件 : #include

      函數定義: int pthread_join(pthread_t thread, void **retval);

      描述 :pthread_join()函數,以阻塞的方式等待thread指定的線程結束。當函數返回時,被等待線程的資源被收回。如果線程已經結束,那么該函數會立即返回。并且thread指定的線程必須是joinable的。

      參數 :thread: 線程標識符,即線程ID,標識唯一線程。retval: 用戶定義的指針,用來存儲被等待線程的返回值。

      返回值 : 0代表成功。 失敗,返回的則是錯誤號。

      例程6

      程序目的:線程正常退出,接受線程退出的返回碼

      程序名稱:pthread_exit.c

      執行代碼如下:

      #include

      #include

      #include

      void *create(void *arg)

      {

      printf("new thread is created ... \n");

      return (void *)0;

      }

      int main(int argc,char *argv[])

      {

      pthread_t tid;

      int error;

      void *temp;

      error = pthread_create(&tid, NULL, create, NULL);

      if( error )

      {

      printf("thread is not created ... \n");

      return -1;

      }

      error = pthread_join(tid, &temp);

      if( error )

      {

      printf("thread is not exit ... \n");

      return -2;

      }

      printf("thread is exit code %d \n", (int )temp);

      return 0;

      }

      執行結果如下:

      fs@ubuntu:~/qiang/thread$ vi thread6.c

      fs@ubuntu:~/qiang/thread$ gcc -o thread6 thread6.c -lpthread

      fs@ubuntu:~/qiang/thread$ ./thread6

      new thread is created ...

      thread is exit code 0

      fs@ubuntu:~/qiang/thread$

      例程總結:

      可以看出來,線程退出可以返回線程的int數值。

      線程退出不僅僅可以返回線程的int數值,還可以返回一個復雜的數據結構

      例程7

      程序目的:線程結束返回一個復雜的數據結構

      代碼如下:

      #include

      #include

      #include

      struct menber

      {

      int a;

      char *b;

      }temp={8,"xiaoqiang"};

      void *create(void *arg)

      {

      printf("new thread ... \n");

      return (void *)&temp;

      }

      int main(int argc,char *argv[])

      {

      int error;

      pthread_t tid;

      struct menber *c;

      error = pthread_create(&tid, NULL, create, NULL);

      if( error )

      {

      printf("new thread is not created ... \n");

      return -1;

      }

      printf("main ... \n");

      error = pthread_join(tid,(void *)&c);

      if( error )

      {

      printf("new thread is not exit ... \n");

      return -2;

      }

      printf("c->a = %d? \n",c->a);

      printf("c->b = %s? \n",c->b);

      sleep(1);

      return 0;

      }

      執行結果如下:

      fs@ubuntu:~/qiang/thread$ gcc -o thread7 thread7.c -lpthread

      fs@ubuntu:~/qiang/thread$ ./thread7

      main ...

      new thread ...

      c->a = 8

      c->b = xiaoqiang

      fs@ubuntu:~/qiang/thread$

      例程總結:

      一定要記得返回的數據結構要是在這個數據要返回的結構沒有釋放的時候應用,如果數據結構已經發生變化,那返回的就不會是我們所需要的,而是臟數據。

      3、線程標識

      函數原型:

      #include

      pthread_t pthread_self(void);

      pid_t getpid(void);

      getpid()用來取得目前進程的進程識別碼,函數說明

      例程8

      程序目的:實現在新建立的線程中打印該線程的id和進程id

      代碼如下:

      #include

      #include

      #include /*getpid()*/

      void *create(void *arg)

      {

      printf("New thread .... \n");

      printf("This thread's id is %u? \n", (unsigned int)pthread_self());

      printf("The process pid is %d? \n",getpid());

      return (void *)0;

      }

      int main(int argc,char *argv[])

      {

      pthread_t tid;

      int error;

      printf("Main thread is starting ... \n");

      error = pthread_create(&tid, NULL, create, NULL);

      if(error)

      {

      printf("thread is not created ... \n");

      return -1;

      }

      printf("The main process's pid is %d? \n",getpid());

      sleep(1);

      return 0;

      }

      執行結果如下:

      fs@ubuntu:~/qiang/thread$ gcc -o thread8 thread8.c -lpthread

      fs@ubuntu:~/qiang/thread$ ./thread8

      Main thread is starting ...

      The main process's pid is 4955

      New thread ....

      This thread's id is 3075853120

      The process pid is 4955

      fs@ubuntu:~/qiang/thread$

      Linux

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      上一篇:紙質文檔管理系統協助辦公 提升工作效率!
      下一篇:云脈文檔識別工具實現文字信息快速識別存儲!
      相關文章
      情人伊人久久综合亚洲| 亚洲中文字幕久久久一区| 久久精品国产亚洲av品善| 亚洲美女大bbbbbbbbb| 亚洲精品成人无码中文毛片不卡| 亚洲日韩精品无码专区网站| 亚洲AV日韩精品一区二区三区| AV激情亚洲男人的天堂国语| 久久久久久久久无码精品亚洲日韩| 一本色道久久88—综合亚洲精品 | 亚洲一区二区三区无码国产| 亚洲欧洲视频在线观看| 亚洲乱码在线播放| 亚洲人成网网址在线看| 亚洲妇女熟BBW| 亚洲精品无码专区在线| 日韩欧美亚洲中文乱码| 亚洲国产天堂久久久久久| 区久久AAA片69亚洲| 亚洲成a人片在线观看无码 | 久久精品国产亚洲AV麻豆王友容| 亚洲国产精品lv| 亚洲日本香蕉视频| 亚洲a级片在线观看| 亚洲人成自拍网站在线观看 | 亚洲精品福利视频| 亚洲毛片基地日韩毛片基地| 亚洲国产精品免费观看| 看亚洲a级一级毛片| 久久亚洲中文字幕精品一区四| 亚洲精品国产精品乱码视色| 亚洲国产精品婷婷久久| 亚洲国产精品午夜电影| 亚洲综合一区国产精品| 国产精品亚洲一区二区三区久久 | 亚洲色大成网站www尤物| 亚洲高清乱码午夜电影网| 亚洲人成网站色在线入口| 久久亚洲国产中v天仙www| 亚洲最大的视频网站| 亚洲欧好州第一的日产suv|