(一)初識線程與進程

一、前言

    最近項目中用到多線程,問題背景是:有個數據採集設備,使用以太網進行通信,一旦建立以太網鏈接之後,該設備自己的內置程序就可以並行的循環接收多個類型的數據(也就是它自己有一個數據接收線程),那麼現在我需要以一定的週期讀取這些數據,並且保證這些數據是該設備最近一次讀取的完整數據,因爲設備接收並保存的數據是一個比較大的數據區段,它自身是用vector保存的,這就涉及到了對一段內存區域的同時讀取操作,即設備自身在不停的寫這個內存區域,而我自己的程序在不停的讀這個區域,這就需要使用多線程同步技術來保證每次讀取的數據都是最新的完整的數據,不能有設備寫一半,然後我的程序讀一半的情況。這也就是多線程編程中的“生產者——消費者”問題。


二、理解線程與進程

    其實說到線程,自然也要解釋什麼是進程,說到進程又要聯繫到程序(即你編輯完成,並能運行的一個具體程序),這三者都有着必然的聯繫。

    首先說說什麼是程序程序是存放在磁盤文件中的可執行目標文件(Windows下是exe文件),想一想是不是?你用IDE新建一個工程,要麼是在桌面創建,要麼是在D、E、F盤創建,總之它們都是磁盤區域。一個程序本身有生命嗎?沒有,只有處理器賦予了它生命力時,即你在電腦上(即操作系統)運行它的時候,他才能成爲一個活動的實體,我們稱這個活動實體爲進程,也就是說,進程是一個“執行中的程序”,它是在操作系統上運行的一個程序實體。

    然後再說說什麼是線程首先,一個線程是運行在一個進程(即一個程序實體)下面的;其次,一個進程實體下可以有多個線程,線程的本質也是一個函數,只是這個函數可以和進程同步執行;接着,其實進程也是一個線程,這裏有兩個概念:主線程對等線程每個進程開始生命週期是都是一個單一的線程,它就是主線程,在某一時刻,主線程可以創建一個新的線程,這個線程就是對等線程,對等線程可以有多個(對等線程下面又可以新建它自己的對等線程,這時對等線程就成了主線程);最後主線程和對等線程的唯一區別僅僅在於主線程是該進程中第一個開始運行的線程,對等線程創建並啓動後就和主線程在同步的運行,如果沒有加入線程控制操作,你無法預測主線程和對等線程的具體執行順序,記住,同步運行和開始運行是兩個不同的概念。

    爲了形象的理解線程和進程,博友可以鏈接這一篇博文,圖文並茂,很好理解進程與線程的一個簡單解釋非常值得鏈接進去看一看)。


三、進程間切換

   前面已經說過,進程是一個“執行中的程序實體” ,如瀏覽器是一個進程,QQ也是一個進程,但是你在一臺電腦上“同時”運行這個兩個進程,即一邊聊天,一般瀏覽網頁,完全滿足了你的多元化需求啊。但這個“同時”只是看起來的“同時”,因爲你在某一時刻還是隻能進行聊天或者瀏覽網頁。如果你的電腦配置不是很高,但你同時打開了很多個進程,如QQ、瀏覽器、視頻播放器、音樂播放器、單機遊戲、VS2010、QT Creator等,剛剛也講過,你在同一時刻也只是在某一具體進程上操作,這是其他進程都在睡覺,等待你去操作它們,可能在你只連續操作一個進程的時候(如使用VS2010編輯程序),你不會覺得電腦卡頓,但是突然你接收到你個好朋友的QQ消息,這時你肯定要去點擊QQ去查看這個消息,這個時候你的配置不高的電腦就會出現卡頓的現象,可能你點擊之後過了半分鐘QQ才響應過來(你我想肯定有過,我反正經常有)。

    再總結一下現象:當你打開多個進程(這就是進程併發),如果某一段時間你只操作一個單獨的進程,你不會覺得電腦卡,但是當你從你正在工作的進程切換到另一個進程時(如從VS2010切換到QQ),電腦就會出現卡頓的現象,半天反應不過來。這是爲什麼呢?這是由於不同進程間的切換需要一定的時間消耗造成的。

    既然進程間可以進行切換,也就是說一個進程是間斷執行的,進程的間斷性,決定了進程具有多種不同的狀態,即實質上進程是在這幾種不同的狀態間進行切換。進程主要有三種狀態:就緒狀態、運行狀態、阻塞狀態。

        (1)就緒狀態

            當一個進程獲得了除處理機以外的一切所需資源,一旦得到處理機即可運行,則稱此進程處於就緒狀態。就緒進程可以按多個優先級來劃分隊列。例如,當一個進程由於時間片用完而進入就緒狀態時,排入低優先級隊列;當進程由I/O操作完成而進入就緒狀態時,排入高優先級隊列。

        (2)運行狀態

            當一個進程在處理機上運行時,則稱該進程處於運行狀態。處於此狀態的進程的數目小於等於處理器的數目,對於單處理機系統,處於運行狀態的進程只有一個。在沒有其他進程可以執行時(如所有進程都在阻塞狀態),通常會自動執行系統的空閒進程。

        (3)阻塞狀態

            這裏詳細說一下進程阻塞狀態,進程阻塞就是說這個進程現在暫停了,它或者是在等待某種條件或事件的發生,在條件滿足或事件到達之前這進程無法繼續執行。進入阻塞狀態的進程會暫時放棄CPU資源,處於暫停狀態(可以理解爲睡眠、等待)。最簡單的進程阻塞例子就是,如果你寫一個打印字符“A”的程序,程序很簡單,如下:

#include <iostream>
#include <string>
using namespace std;
void main()
{
    char a;
    cout << "請從鍵盤輸入字符A: ";
    cin >> a;     //從鍵盤輸入HelloWOrd
    cout << a << endl;
}
你可以試着編輯運行這個程序,出現下面這個界面,提示你輸入字符A:


這時,如果你一直不從鍵盤輸入字符“A”,這個程序(進程)就一直停滯在這裏,也就是說該進程進入了阻塞狀態,但這個進程的阻塞狀態不會影響它自己,也不會影響其他的進程,比如你可以接下來去打開QQ進行聊天程序,直到你想起來你的這個打印字符的程序還沒有完成,你應該去完成它,然後你用鼠標選中控制檯,從鍵盤輸入字符“A”,這個程序就繼續運行,打印字符“A”,然後程序運行完畢,進程結束。


    在上面這個例子中,就包含了上述進程的三個狀態。具體爲,程序編輯完成之後,你點擊運行按鈕,運行該程序,這個程序就經歷了就緒狀態和運行狀態,這兩個狀態在這個例子中是連續完成的,只是我們感覺不到,運行之後,然後程序在控制檯打印“請從鍵盤輸入字符A:”提示你輸入字符,到這裏這個進程就進入阻塞狀態,然後你去幹其他事情(QQ聊天),這個進程就繼續阻塞(其實這也是一種就緒狀態),接着你在控制檯中輸入字符“A”,這進程就結束阻塞狀態,切換到運行狀態,完成整個進程的工作,打印字符“A”。

    最後,簡單說說爲什麼進程間切換會出現電腦卡頓的現象,因爲各個進程具有獨立的運行環境(即與這個進程運行相關的各種資源),進程間切換的動作需要保存當前進程的信息,恢復下一個運行進程的信息,然後把系統使用權轉交給新的運行進程,這個過程是需要一定的時間的,所以會出現電腦卡頓現象。


四、線程狀態

    進程具有不同的狀態,同樣線程也具有不同的狀態,總的來說,線程從創建、運行到結束總是處於以下五個狀態中,並在其中不停的切換,具體五中狀態爲:新建狀態、就緒狀態、運行狀態、阻塞狀態及死亡狀態,各個狀態的理解與進程狀態類似。

    這裏還是詳細說一下線程的阻塞狀態,本質上來講,線程的阻塞狀態還是和進程的阻塞狀態一樣理解, 在線程運行過程中,可能由於各種原因進入阻塞狀態:
        1>線程通過調用sleep方法進入睡眠狀態;
        2>線程調用一個在I/O上被阻塞的操作,即該操作在輸入輸出操作完成之前不會返回到它的調用者;
        3>線程試圖得到一個鎖,而該鎖正被其他線程持有;
        4>線程在等待某個觸發條件;
        ......           

    也就是說一個線程進入阻塞狀態,也是指這個線程本身暫停了,但它還沒有結束運行,只是暫時讓出CPU資源,這時其他處於就緒狀態的線程就可以獲得CPU資源,進入運行狀態,即一個線程進入阻塞狀態不影響其他的對等線程執行。

    線程由於它本身是在一個進程中運行,所以線程間切換比進程間切換需要保存和恢復的信息要少很多,所以線程間切換要比進程間切換快很多。



發佈了40 篇原創文章 · 獲贊 127 · 訪問量 18萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章