c++多進程編程

c++多進程編程

介紹

  • 進程:進程是一個正在執行的程序,是向CPU申請資源的,進程之間數據相互獨立,一個進程至少有一個線程。
  • 線程:線程是進程中的單一的順序控制流程也可以叫做最小控制單元,線程是進程中執行單元,開啓一個線程比開啓一個進程更加節省資源。
  • 多線程:多線程是多任務處理的一種特殊形式,多任務處理允許讓電腦同時運行兩個或兩個以上的程序。一般情況下,兩種類型的多任務處理:基於進程和基於線程。
    多線程程序包含可以同時運行的兩個或多個部分。這樣的程序中的每個部分稱爲一個線程,每個線程定義了一個單獨的執行路徑。
  • 基於進程的多任務處理是程序的併發執行。
  • 基於線程的多任務處理是同一程序的片段的併發執行。

線程

線程的優點:

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

線程的缺點

性能損失

一個很少被外部事件阻塞的計算密集型線程往往無法與共它線程共享同一個處理器。如果計算密集型線程的數量比可用的處理器多,那麼可能會有較大的性能損失,這裏的性能損失指的是增加了額外的同步和調度開銷,而可用的資源不變。

健壯性降低

編寫多線程需要更全面更深入的考慮,在一個多線程程序裏,因時間分配上的細微偏差或者因共享了不該共享的變量而造成不良影響的可能性是很大的,換句話說線程之間是缺乏保護的。

缺乏訪問控制

進程是訪問控制的基本粒度,在一個線程中調用某些OS函數會對整個進程造成影響。

編程難度提高

編寫與調試一個多線程程序比單線程程序困難得多。

創建線程

#include <pthread.h>
pthread_create (thread, attr, start_routine, arg) 
參數 描述
thread 指向線程標識符指針
attr 一個不透明的屬性對象,可以被用來設置線程屬性。您可以指定線程屬性對象,也可以使用默認值 NULL
start_routine 線程運行函數起始地址,一旦線程被創建就會執行
arg 運行函數的參數。它必須通過把引用作爲指針強制轉換爲 void 類型進行傳遞。如果沒有傳遞參數,則使用 NULL

創建線程成功時,函數返回 0,若返回值不爲 0 則說明創建線程失敗。

終止線程

#include <pthread.h>
pthread_exit (status) 

在這裏,pthread_exit 用於顯式地退出一個線程。通常情況下,pthread_exit() 函數是在線程完成工作後無需繼續存在時被調用。
如果 main() 是在它所創建的線程之前結束,並通過 pthread_exit() 退出,那麼其他線程將繼續執行。否則,它們將在 main() 結束時自動被終止。

程序示例

#include <iostream>
// 必須的頭文件
#include <pthread.h>
using namespace std; 
#define NUM_THREADS 5 
// 線程的運行函數
void* say_hello(void* args)
{
    cout << "Hello Runoob!" << endl;
    return 0;
}
int main()
{
    // 定義線程的 id 變量,多個變量使用數組
    pthread_t tids[NUM_THREADS];
    for(int i = 0; i < NUM_THREADS; ++i)
    {
        //參數依次是:創建的線程id,線程參數,調用的函數,傳入的函數參數
        int ret = pthread_create(&tids[i], NULL, say_hello, NULL);
        if (ret != 0)
        {
           cout << "pthread_create error: error_code=" << ret << endl;
        }
    }
    //等各個線程退出後,進程才結束,否則進程強制結束了,線程可能還沒反應過來;
    pthread_exit(NULL);
}
g++ test.cpp -lpthread -o test        #linux編譯指令
#include <iostream>
#include <cstdlib>
#include <pthread.h>
using namespace std; 
#define NUM_THREADS     5
void *PrintHello(void *threadid)
{  
   // 對傳入的參數進行強制類型轉換,由無類型指針變爲整形數指針,然後再讀取
   int tid = *((int*)threadid);
   cout << "Hello Runoob! 線程 ID, " << tid << endl;
   pthread_exit(NULL);
}
int main ()
{
   pthread_t threads[NUM_THREADS];
   int indexes[NUM_THREADS];// 用數組來保存i的值
   int rc;
   int i;
   for( i=0; i < NUM_THREADS; i++ ){      
      cout << "main() : 創建線程, " << i << endl;
      indexes[i] = i; //先保存i的值
      // 傳入的時候必須強制轉換爲void* 類型,即無類型指針        
      rc = pthread_create(&threads[i], NULL, 
                          PrintHello, (void *)&(indexes[i]));
      if (rc){
         cout << "Error:無法創建線程," << rc << endl;
         exit(-1);
      }
   }
   pthread_exit(NULL);
}
main() : 創建線程, 0
main() : 創建線程, 1
main() : 創建線程, 2
main() : 創建線程, 3
main() : 創建線程, Hello Runoob! 線程 ID, 0
4
Hello Runoob! 線程 ID, Hello Runoob! 線程 ID, 3
Hello Runoob! 線程 ID, 1
Hello Runoob! 線程 ID, 4
2

連接和分離線程

pthread_join (threadid, status) 
pthread_detach (threadid) 

pthread_join() 子程序阻礙調用程序,直到指定的 threadid 線程終止爲止。當創建一個線程時,它的某個屬性會定義它是否是可連接的(joinable)或可分離的(detached)。只有創建時定義爲可連接的線程纔可以被連接。如果線程創建時被定義爲可分離的,則它永遠也不能被連接。

#include <iostream>
#include <cstdlib>
#include <pthread.h>
#include <unistd.h>
 
using namespace std;
 
#define NUM_THREADS     5
 
void *wait(void *t)
{
   int i;
   long tid;
 
   tid = (long)t;
 
   sleep(1);
   cout << "Sleeping in thread " << endl;
   cout << "Thread with id : " << tid << "  ...exiting " << endl;
   pthread_exit(NULL);
}
 
int main ()
{
   int rc;
   int i;
   pthread_t threads[NUM_THREADS];
   pthread_attr_t attr;
   void *status;
   // 初始化並設置線程爲可連接的(joinable)
   pthread_attr_init(&attr);
   pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
   for( i=0; i < NUM_THREADS; i++ ){
      cout << "main() : creating thread, " << i << endl;
      rc = pthread_create(&threads[i], NULL, wait, (void *)&i );
      if (rc){
         cout << "Error:unable to create thread," << rc << endl;
         exit(-1);
      }
   } 
   // 刪除屬性,並等待其他線程
   pthread_attr_destroy(&attr);
   for( i=0; i < NUM_THREADS; i++ ){
      rc = pthread_join(threads[i], &status);
      if (rc){
         cout << "Error:unable to join," << rc << endl;
         exit(-1);
      }
      cout << "Main: completed thread id :" << i ;
      cout << "  exiting with status :" << status << endl;
   } 
   cout << "Main: program exiting." << endl;
   pthread_exit(NULL);
}

進程的三種基本狀態:

(1) 就緒狀態:進程已獲得除CPU外的所有必要資源,只等待CPU時的狀態。一個系統會將多個處於就緒狀態的進程排成一個就緒隊列。
(2) 執行狀態:進程已獲CPU,正在執行。單處理機系統中,處於執行狀態的進程只一個;多處理機系統中,有多個處於執行狀態的進程。
(3) 阻塞狀態:正在執行的進程由於某種原因而暫時無法繼續執行,便放棄處理機而處於暫停狀態,即進程執行受阻。(這種狀態又稱等待狀態或封鎖狀態)

進程的操作

創建進程有兩種方式,一是由操作系統創建;二是由父進程創建。操作系統創建的進程,它們之間是平等的,一般不存在資源繼承關係。而由父進程創建的進程(子進程),它們和父進程存在隸屬關係。子進程又可以創建進程,形成一個進程家族。
fork()函數調用後有2個返回值,調用一次,返回兩次。成功調用fork函數後,當前進程實際上已經分裂爲兩個進程,一個是原來的父進程,另一個是剛剛創建的子進程。fork()函數的2個返回值,一個是父進程調用fork函數後的返回值,該返回值是剛剛創建的子進程的ID;另一個是子進程中fork函數的返回值,該返回值是0。這樣可以用返回值來區分父、子進程。

進程的編程示例

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <iostream>
#include <string>
using namespace std;
main()
{
    pid_t pid;
    char *msg;
    int k;
    pid=fork();
    switch(pid){
        //子進程執行部分
		case 0:
            msg="Child process is running.\n";
            k=3;
            break;
        case -1:
            perror("Process creation failed.\n");
            break;
        //父進程執行部分
		default:
            msg="Parent process is running.\n";
            k=5;
            break;
    }
	//父子進程共同執行部分
    while(k>0){
        puts(msg);
        sleep(1);
        k--;
    }
}
Parent process is running.
Child process is running.
Parent process is running.
Child process is running.
Parent process is running.
Child process is running.
Parent process is running.
Parent process is running.

注意事項

父子進程終止的先後順序不同會產生不同的結果。

  • 在子進程退出前父進程先退出,則系統會讓init進程接管子進程。
  • 當子進程先於父進程終止,而父進程又沒有調用wait函數等待子進程結束,子進程進入殭屍狀態,並且會一直保持下去除非系統重啓。子進程處於殭屍狀態時,內核只保存該進程的一些必要信息以備父進程所需。此時子進程始終佔用着資源,同時也減少了系統可以創建的最大進程數。如果子進程先於父進程終止,且父進程調用了wait或waitpid函數,則父進程會等待子進程結束。
  • 在Linux下,可以簡單地將SIGCHLD信號的操作設爲SIG_IGN,這樣當子進程結束時就不會稱爲殭屍進程。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章