天天看點

UNIX(程序間通信):06 深入了解程序,線程和協程

一、程序

  程序,直覺點說,儲存在硬碟上的程式運作以後,會在記憶體空間裡形成一個獨立的記憶體體,這個記憶體體有自己獨立的位址空間,有自己的堆,上級挂靠機關是作業系統。作業系統會以程序為機關,配置設定系統資源(CPU時間片、記憶體等資源),程序是資源配置設定的最小機關。

【程序間通信(IPC)】:

  • 管道(Pipe)、命名管道(FIFO)、消息隊列(Message Queue) 、信号量(Semaphore) 、共享記憶體(Shared Memory);套接字(Socket)。

二、線程

  線程,有時被稱為輕量級程序(Lightweight Process,LWP),是作業系統排程(CPU排程)執行的最小機關。

三、程序和線程的差別與聯系

【差別】:

  • 排程:線程作為排程和配置設定的基本機關,程序作為擁有資源的基本機關;
  • 并發性:不僅程序之間可以并發執行,同一個程序的多個線程之間也可并發執行;
  • 擁有資源:程序是擁有資源的一個獨立機關,線程不擁有系統資源,但可以通路隸屬于程序的資源。程序所維護的是程式所包含的資源(靜态資源), 如:位址空間,打開的檔案句柄集,檔案系統狀态,信号處理handler等;線程所維護的運作相關的資源(動态資源),如:運作棧,排程相關的控制資訊,待處理的信号集等;
  • 系統開銷:在建立或撤消程序時,由于系統都要為之配置設定和回收資源,導緻系統的開銷明顯大于建立或撤消線程時的開銷。但是程序有獨立的位址空間,一個程序崩潰後,在保護模式下不會對其它程序産生影響,而線程隻是一個程序中的不同執行路徑。線程有自己的堆棧和局部變量,但線程之間沒有單獨的位址空間,一個程序死掉就等于所有的線程死掉,是以多程序的程式要比多線程的程式健壯,但在程序切換時,耗費資源較大,效率要差一些。
  • 上下文切換:在某一時刻,一核CPU隻能執行一個程序。相當于核心隻能讓一核CPU為一個機器人提供驅動力讓它動起來(1:1),多核CPU可以同時為多個機器人提供驅動力。
  • 但是作業系統上有很多機器人在幹活,是以核心要控制CPU不斷的為不同機器人來回提供驅動力,這是程序切換(這是站在核心的角度上看的,也叫上下文切換)
  • 為了讓你感覺機器人沒有停止工作,核心控制隻給每個機器人一點點的CPU時間片。這就相當于轉起來的電扇,你感覺沒有間隙,其實是有的,隻是間隙時間太短,人眼難辨。現在可以腦部一下,一大堆的機器人在你面前完全不停的跳着鬼步舞....
  • 因為CPU要切換給不同的機器人提供驅動力,是以每次切換之前的機器人幹活到了哪裡以及它的狀态得記錄下來,這是上下文保留(保護現場)
  • 保護現場是必要的額外的工作,頻繁上下文切換會浪費CPU在這些額外工作上 

【聯系】:

  • 一個線程隻能屬于一個程序,而一個程序可以有多個線程,但至少有一個線程;
  • 資源配置設定給程序,同一程序的所有線程共享該程序的所有資源;
  • 處理機分給線程,即真正在處理機上運作的是線程;
  • 線程在執行過程中,需要協作同步。不同程序的線程間要利用消息通信的辦法實作同步。

四、一個形象的例子解釋程序和線程的差別

  這副圖是一個雙向多車道的道路圖,假如我們把整條道路看成是一個“程序”的話,那麼圖中由白色虛線分隔開來的各個車道就是程序中的各個“線程”了。

  • 這些線程(車道)共享了程序(道路)的公共資源(土地資源)。
  • 這些線程(車道)必須依賴于程序(道路),也就是說,線程不能脫離于程序而存在(就像離開了道路,車道也就沒有意義了)。
  • 這些線程(車道)之間可以并發執行(各個車道你走你的,我走我的),也可以互相同步(某些車道在交通燈亮時禁止繼續前行或轉彎,必須等待其它車道的車輛通行完畢)。
  • 這些線程(車道)之間依靠代碼邏輯(交通燈)來控制運作,一旦代碼邏輯控制有誤(死鎖,多個線程同時競争唯一資源),那麼線程将陷入混亂,無序之中。
  • 這些線程(車道)之間誰先運作是未知的,隻有線上程剛好被配置設定到CPU時間片(交通燈變化)的那一刻才能知道。

五、程序/線程之間的親緣性

  親緣性的意思是程序/線程隻在某個cpu上運作(多核系統),比如:

BOOL WINAPI SetProcessAffinityMask(
  _In_ HANDLE    hProcess,
  _In_ DWORD_PTR dwProcessAffinityMask
);
/*
dwProcessAffinityMask 如果是 0 , 代表目前程序隻在cpu0 上工作;
如果是 0x03 , 轉為2進制是 00000011 . 代表隻在 cpu0 或 cpu1上工作;
*/      

  使用CPU親緣性的好處:設定CPU親緣性是為了防止程序/線程在CPU的核上頻繁切換,進而避免因切換帶來的CPU的L1/L2 cache失效,cache失效會降低程式的性能。

六、協程

  協程,是一種比線程更加輕量級的存在,協程不是被作業系統核心所管理,而完全是由程式所控制(也就是在使用者态執行)。這樣帶來的好處就是性能得到了很大的提升,不會像線程切換那樣消耗資源。

  子程式,或者稱為函數,在所有語言中都是層級調用,比如A調用B,B在執行過程中又調用了C,C執行完畢傳回,B執行完畢傳回,最後是A執行完畢。是以子程式調用是通過棧實作的,一個線程就是執行一個子程式。子程式調用總是一個入口,一次傳回,調用順序是明确的。而協程的調用和子程式不同。

  協程在子程式内部是可中斷的,然後轉而執行别的子程式,在适當的時候再傳回來接着執行。

def A():
    print '1'
    print '2'
    print '3'

def B():
    print 'x'
    print 'y'
    print 'z'      
  • 極高的執行效率:因為子程式切換不是線程切換,而是由程式自身控制,是以,沒有線程切換的開銷,和多線程比,線程數量越多,協程的性能優勢就越明顯;
  • 不需要多線程的鎖機制:因為隻有一個線程,也不存在同時寫變量沖突,在協程中控制共享資源不加鎖,隻需要判斷狀态就好了,是以執行效率比多線程高很多。

繼續閱讀