淺談多進程多線程的選擇

線程進程的區(qū)別體現(xiàn)在幾個方面:

  1. 因為進程擁有獨立的堆棧空間和數據段,所以每當啟動一個新的進程必須分配給它獨立的地址空間,建立眾多的數據表來維護它的代碼段、堆棧段和數據段,這對于多進程來說十分“奢侈”,系統(tǒng)開銷比較大,而線程不一樣,線程擁有獨立的堆棧空間,但是共享數據段,它們彼此之間使用相同的地址空間,共享大部分數據,比進程更節(jié)儉,開銷比較小,切換速度也比進程快,效率高,但是正由于進程之間獨立的特點,使得進程安全性比較高,也因為進程有獨立的地址空間,一個進程崩潰后,在保護模式下不會對其它進程產生影響,而線程只是一個進程中的不同執(zhí)行路徑。一個線程死掉就等于整個進程死掉
  2. 體現(xiàn)在通信機制上面,正因為進程之間互不干擾,相互獨立,進程的通信機制相對很復雜,譬如管道,信號,消息隊列,共享內存,套接字等通信機制,而線程由于共享數據段所以通信機制很方便。

進程與線程的選擇取決以下幾點:

  1. 需要頻繁創(chuàng)建銷毀情況:優(yōu)先使用線程;因為對進程來說創(chuàng)建和銷毀一個進程代價是很大的。
  2. 大量計算,頻繁切換:線程的切換速度快,所以在需要大量計算切換頻繁時用線程,還有耗時的操作使用線程可提高應用程序的響應
  3. 并行:并行操作時使用線程,如C/S 的服務器端并發(fā)線程響應用戶的請求;
  4. 多機和多核:因為對CPU系統(tǒng)的效率使用上線程更占優(yōu),所以可能要發(fā)展到多機分布的用進程,多核分布用線程;
  5. 安全性:需要更穩(wěn)定安全時,適合選擇進程(例如:守護進程模式);需要速度時,選擇線程更好。

魚還是熊掌:淺談多進程多線程的選擇

https://www.cnblogs.com/virusolf/p/5458325.html
關于多進程和多線程,教科書上最經典的一句話是“進程是資源分配的最小單位,線程是CPU調度的最小單位”,這句話應付考試基本上夠了,但如果在工作中遇到類似的選擇問題,那就沒有這么簡單了,選的不好,會讓你深受其害。

經常在網絡上看到有的XDJM問“多進程好還是多線程好?”、“Linux下用多進程還是多線程?”等等期望一勞永逸的問題,我只能說:沒有最好,只有更好。根據實際情況來判斷,哪個更加合適就是哪個好。

我們按照多個不同的維度,來看看多線程和多進程的對比(注:因為是感性的比較,因此都是相對的,不是說一個好得不得了,另外一個差的無法忍受)。


image.png

1)需要頻繁創(chuàng)建銷毀的優(yōu)先用線程

原因請看上面的對比。
這種原則最常見的應用就是Web服務器了,來一個連接建立一個線程,斷了就銷毀線程,要是用進程,創(chuàng)建和銷毀的代價是很難承受的

2)需要進行大量計算的優(yōu)先使用線程

所謂大量計算,當然就是要耗費很多CPU,切換頻繁了,這種情況下線程是最合適的。
這種原則最常見的是圖像處理、算法處理。

3)強相關的處理用線程,弱相關的處理用進程

什么叫強相關、弱相關?理論上很難定義,給個簡單的例子就明白了。
一般的Server需要完成如下任務:消息收發(fā)、消息處理。“消息收發(fā)”和“消息處理”就是弱相關的任務,而“消息處理”里面可能又分為“消息解碼”、“業(yè)務處理”,這兩個任務相對來說相關性就要強多了。因此“消息收發(fā)”和“消息處理”可以分進程設計,“消息解碼”、“業(yè)務處理”可以分線程設計。
當然這種劃分方式不是一成不變的,也可以根據實際情況進行調整。

4)可能要擴展到多機分布的用進程,多核分布的用線程

原因請看上面對比。

5)都滿足需求的情況下,用你最熟悉、最拿手的方式

至于“數據共享、同步”、“編程、調試”、“可靠性”這幾個維度的所謂的“復雜、簡單”應該怎么取舍,我只能說:沒有明確的選擇方法。但我可以告訴你一個選擇原則:如果多進程和多線程都能夠滿足要求,那么選擇你最熟悉、最拿手的那個。
需要提醒的是:雖然我給了這么多的選擇原則,但實際應用中基本上都是“進程+線程”的結合方式,千萬不要真的陷入一種非此即彼的誤區(qū)。

消耗資源:

從內核的觀點看,進程的目的就是擔當分配系統(tǒng)資源(CPU時間、內存等)的基本單位。線程是進程的一個執(zhí)行流,是CPU調度和分派的基本單位,它是比進程更小的能獨立運行的基本單位。
線程,它們彼此之間使用相同的地址空間,共享大部分數據,啟動一個線程所花費的空間遠遠小于啟動一個進程所花費的空間,而且,線程間彼此切換所需的時間也遠遠小于進程間切換所需要的時間。據統(tǒng)計,總的說來,一個進程的開銷大約是一個線程開銷的30倍左右,當然,在具體的系統(tǒng)上,這個數據可能會有較大的區(qū)別。

通訊方式:

進程之間傳遞數據只能是通過通訊的方式,即費時又不方便。線程時間數據大部分共享(線程函數內部不共享),快捷方便。但是數據同步需要鎖,對于static變量尤其注意
線程自身優(yōu)勢:
提高應用程序響應;使多CPU系統(tǒng)更加有效。操作系統(tǒng)會保證當線程數不大于CPU數目時,不同的線程運行于不同的CPU上;
改善程序結構。一個既長又復雜的進程可以考慮分為多個線程,成為幾個獨立或半獨立的運行部分,這樣的程序會利于理解和修改。

實驗數據:
進程實驗代碼(fork.c):

#include <stdlib.h>
#include <stdio.h>
#include <signal.h>

#define P_NUMBER 255 //并發(fā)進程數量
#define COUNT 5 //每次進程打印字符串數
#define TEST_LOGFILE "logFile.log"
FILE *logFile=NULL;

char *s="hello linux\0";

int main()
{
    int i=0,j=0;
    logFile=fopen(TEST_LOGFILE,"a+");//打開日志文件
    for(i=0;i<P_NUMBER;i++)
    {
        if(fork()==0)//創(chuàng)建子進程,if(fork()==0){}這段代碼是子進程運行區(qū)間
        {
            for(j=0;j<COUNT;j++)
            {
                printf("[%d]%s\n",j,s);//向控制臺輸出
                /*當你頻繁讀寫文件的時候,Linux內核為了提高讀寫性能與速度,會將文件在內存中進行緩存,這部分內存就是Cache Memory(緩存內存)。可能導致測試結果不準,所以在此注釋*/
                //fprintf(logFile,"[%d]%s\n",j,s);//向日志文件輸出,
            }
            exit(0);//子進程結束
        }
    }
    
    for(i=0;i<P_NUMBER;i++)//回收子進程
    {
        wait(0);
    }
    
    printf("Okay\n");
    return 0;
}

線程實驗代碼(thread.c):

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

#define P_NUMBER 255//并發(fā)線程數量
#define COUNT 5 //每線程打印字符串數
#define TEST_LOG "logFile.log"
FILE *logFile=NULL;

char *s="hello linux\0";

print_hello_linux()//線程執(zhí)行的函數
{
    int i=0;
    for(i=0;i<COUNT;i++)
    {
        printf("[%d]%s\n",i,s);//想控制臺輸出
        /*當你頻繁讀寫文件的時候,Linux內核為了提高讀寫性能與速度,會將文件在內存中進行緩存,這部分內存就是Cache Memory(緩存內存)。可能導致測試結果不準,所以在此注釋*/
        //fprintf(logFile,"[%d]%s\n",i,s);//向日志文件輸出
    }
    pthread_exit(0);//線程結束
}

int main()
{
    int i=0;
    pthread_t pid[P_NUMBER];//線程數組
    logFile=fopen(TEST_LOG,"a+");//打開日志文件
    
    for(i=0;i<P_NUMBER;i++)
        pthread_create(&pid[i],NULL,(void *)print_hello_linux,NULL);//創(chuàng)建線程
        
    for(i=0;i<P_NUMBER;i++)
        pthread_join(pid[i],NULL);//回收線程
        
    printf("Okay\n");
    return 0;
}

兩段程序做的事情是一樣的,都是創(chuàng)建“若干”個進程/線程,每個創(chuàng)建出的進程/線程打印“若干”條“hello linux”字符串到控制臺和日志文件,兩個“若干”由兩個宏 P_NUMBER和COUNT分別定義,程序編譯指令如下:

gcc -o fork fork.c
gcc -lpthread -o thread thread.c

實驗通過time指令執(zhí)行兩個程序,抄錄time輸出的掛鐘時間(real時間):

time ./fork
time ./thread
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。