多進程/多線程

這裏寫圖片描述
進程是表示資源分配的基本單位,又是調度運行的基本單位。
線程是進程中執行運算的最小單位,亦即執行CPU調度的基本單位。

一,適用場景:
1>需要大量的計算
如果你在實際的應用中需要大量的計算,那麼你可以優先使用線程。由於大量的計算會耗費很多的CPU並且切換回很頻繁。而我們上文中也有說到,線程的切換簡單而且CPU的利用率高。
2>需要頻繁創建銷燬
如果你實際應用中需要大量的計算,那你可以優先選擇使用線程。因爲線程的創建銷燬比進程的都要簡單方便。就比如常見的web服務器,來一個連接就建立一個進程,然後斷了就進行銷燬。但是由於進程的創建和銷燬很麻煩,所以一般都會選用線程。
3>強相關、弱相關
我們先看一個例子,一般的Server需要完成如下任務:消息收發、消息處理。“消息收發”和“消息處理”就是弱相關的任務,而“消息處理”裏面可能又分爲“消息解碼”、“業務處理”,這兩個任務相對來說相關性就要強多了。
而一般來說強相關的處理使用線程,弱相關的處理使用進程。
4>多機、多核分佈
線程使用於多核分佈式,所以多機分佈使用進程,多核分佈使用線程。

二,代碼

多進程代碼:
#include <stdlib.h> 
#include <sys/types.h> 
#include <unistd.h> 

int main() 
{ 
  pid_t child_pid; 

  /* 創建一個子進程 */ 
  child_pid = fork(); 
  if(child_pid == 0) 
  { 
    printf("child pid\n"); 
    exit(0); 
  } 
  else
  { 
    printf("father pid\n"); 
    sleep(60); 
  } 

  return 0; 
} 

進程相關問題:
1>進程間通信:
1)管道:
管道分爲有名管道和無名管道
無名管道是一種半雙工的通信方式,數據只能單向流動,而且只能在具有親緣關係的進程間使用.進程的親緣關係一般指的是父子關係。無明管道一般用於兩個不同進程之間的通信。當一個進程創建了一個管道,並調用fork創建自己的一個子進程後,父進程關閉讀管道端,子進程關閉寫管道端,這樣提供了兩個進程之間數據流動的一種方式。
有名管道也是一種半雙工的通信方式,但是它允許無親緣關係進程間的通信。

無名管道:優點:簡單方便;缺點:1)侷限於單向通信2)只能創建在它的進程以及其有親緣關係的進程之間;3)緩衝區有限;
有名管道:優點:可以實現任意關係的進程間的通信;缺點:1)長期存於系統中,使用不當容易出錯;2)緩衝區有限
2)信號量:
信號量是一個計數器,可以用來控制多個線程對共享資源的訪問.,它不是用於交換大批數據,而用於多線程之間的同步.它常作爲一種鎖機制,防止某進程在訪問資源時其它進程也訪問該資源.因此,主要作爲進程間以及同一個進程內不同線程之間的同步手段.
優點:可以同步進程;缺點:信號量有限

3)信號:
信號是一種比較複雜的通信方式,用於通知接收進程某個事件已經發生.

4)消息隊列:
消息隊列是消息的鏈表,存放在內核中並由消息隊列標識符標識.消息隊列克服了信號傳遞信息少,管道只能承載無格式字節流以及緩衝區大小受限等特點.消息隊列是UNIX下不同進程之間可實現共享資源的一種機制,UNIX允許不同進程將格式化的數據流以消息隊列形式發送給任意進程.對消息隊列具有操作權限的進程都可以使用msget完成對消息隊列的操作控制.通過使用消息類型,進程可以按任何順序讀信息,或爲消息安排優先級順序.

優點:可以實現任意進程間的通信,並通過系統調用函數來實現消息發送和接收之間的同步,無需考慮同步問題,方便;缺點:信息的複製需要額外消耗CPU的時間,不適宜於信息量大或操作頻繁的場合
5)共享內存:
共享內存就是映射一段能被其他進程所訪問的內存,這段共享內存由一個進程創建,但多個進程都可以訪問.共享內存是最快的IPC(進程間通信)方式,它是針對其它進程間通信方式運行效率低而專門設計的.它往往與其他通信機制,如信號量,配合使用,來實現進程間的同步與通信.

優點:無須複製,快捷,信息量大;缺點:1)通信是通過將共無法實現享空間緩衝區直接附加到進程的虛擬地址空間中來實現的,因此進程間的讀寫操作的同步問題;2)利用內存緩衝區直接交換信息,內存的實體存在於計算機中,只能同一個計算機系統中的諸多進程共享,不方便網絡通信

6)套接字:可用於不同及其間的進程通信
優點:1)傳輸數據爲字節級,傳輸數據可自定義,數據量小效率高;2)傳輸數據時間短,性能高;3) 適合於客戶端和服務器端之間信息實時交互;4) 可以加密,數據安全性強
缺點:1) 需對傳輸的數據進行解析,轉化成應用級的數據。
2>僵死進程:
孤兒進程:一個父進程退出,而它的一個或多個子進程還在運行,那麼那些子進程將成爲孤兒進程。孤兒進程將被init進程(進程號爲1)所收養,並由init進程對它們完成狀態收集工作。
殭屍進程:一個進程使用fork創建子進程,如果子進程退出,而父進程並沒有調用wait或waitpid獲取子進程的狀態信息,那麼子進程的進程描述符仍然保存在系統中。這種進程稱之爲僵死進程。

殭屍進程解決辦法:
1>通過信號機制
子進程退出時向父進程發送SIGCHILD信號,父進程處理SIGCHILD信號。在信號處理函數中調用wait進行處理殭屍進程。
2>原理是將子進程成爲孤兒進程,從而其的父進程變爲init進程,通過init進程可以處理殭屍進程。即:fork()兩次。

多線程 生產者-消費者模式:
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <stdlib.h>
#include <iostream>
using namespace std;

//初始化互斥鎖和條件變量
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond =PTHREAD_COND_INITIALIZER;

typedef struct listNode Node;
struct listNode
{
    int num;
    struct listNode * pNext;
};

Node *phead=NULL;//define the head point;

void * producter(void * arg)
{
    for(;;)
    {
        //只產生一個節點,即停止生產
        while(phead==NULL)
        {
            //產生一個新的節點
            Node *tmpNode=(Node*)malloc(sizeof(Node));
            tmpNode->num=rand()%1000+1;
            pthread_mutex_lock(&mutex);
            //把節點加入到鏈表
            tmpNode->pNext=phead;//頭插法,即新節點的指針域指向鏈表的頭指針
            phead=tmpNode;//新節點的指針變爲鏈表的頭指針。
            cout<<"producing the num = "<<tmpNode->num<<endl;
            pthread_mutex_unlock(&mutex);
            //添加結束,發送信號
            pthread_cond_signal(&cond);
        }
        //  sleep(rand()%1);//方便觀察
    }
    return NULL;
}

void * consumer(void * arg)
{
    for (;;)
    {
        pthread_mutex_lock(&mutex);
        //判斷鏈表是否爲空,如果爲空,則調用條件變量函數,阻塞線程
        while (phead==NULL)
        {
            //阻塞線程,釋放鎖,收到signal後,釋放線程,獲得鎖
            pthread_cond_wait(&cond,&mutex);
        }
        //如果不爲空,則用頭刪法刪除鏈表的頭節點
        //定義一個臨時節點保存被刪除的頭部節點
        Node *tmpNode=phead;
        phead=phead->pNext;//頭節點的指針域存的地址作爲新的頭節點
        cout<<"---------------consumer--------"<<tmpNode->num<<endl;
        free(tmpNode);
        pthread_mutex_unlock(&mutex);
    }
    return NULL;
}

int main(void)
{
    //定義並創建生產者和消費者線程
    pthread_t ptid,ctid;
    pthread_create(&ptid,NULL,producter,NULL);
    pthread_create(&ctid,NULL,consumer,NULL);

    //等待線程結束
    pthread_join(ptid,NULL);
    pthread_join(ctid,NULL);

    return 0;

}

線程相關問題:
1.線程之間的通信方式?
# 鎖機制:包括互斥鎖、條件變量、讀寫鎖
*互斥鎖提供了以排他方式防止數據結構被併發修改的方法。
*讀寫鎖允許多個線程同時讀共享數據,而對寫操作是互斥的。
*條件變量可以以原子的方式阻塞進程,直到某個特定條件爲真爲止。對條件的測試是在互斥鎖的保護下進行的。條件變量始終與互斥鎖一起使用。
線程間的通信目的主要是用於線程同步,所以線程沒有像進程通信中的用於數據交換的通信機制。
2.多線程的同步和互斥:
用戶模式下的方法有:原子操作(例如一個單一的全局變量),臨界區。
內核模式下的方法有:事件,信號量,互斥量。
1、臨界區:通過對多線程的串行化來訪問公共資源或一段代碼,速度快,適合控制數據訪問。
2、互斥量:爲協調共同對一個共享資源的單獨訪問而設計的。
3、信號量:爲控制一個具有有限數量用戶資源而設計。
4、事 件:用來通知線程有一些事件已發生,從而啓動後繼任務的開始。

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