天天看點

從代碼分析多線程工作原理

##本半仙嘔心瀝血所作,能閱讀此卷實乃汝之所幸。

什麼是程序:程序是指在系統中正在運作的一個應用程式,每個程序之間是獨立,且運作在其專用且受保護的記憶體空間内。如開打的一個exe就是一個程序。

什麼是線程:程序要想執行任務,必須得有線程。每個程序至少有一個線程,線程是程序的基本執行單元,一個程序的所有任務都是線上程中執行。同一個程序的各個線程可以共享該程序所擁有的資源,這一點很關鍵,我們可以利用這一點來做到線程之間的通信。

一個程序好比一個工廠,工廠要生産東西就必須要有生産流水線。如果一個工廠隻有一個軀殼而沒有生産流水線是無法生産出産品的。這也就是為什麼一個程序至少有一個線程的原因。但是往往一條生産流水線的工作效率是很有限,需要為工廠多增加幾條生産流水線。即一個程序啟動多個線程了完成任務。屬于工廠的資源,任何一條流水線都可以使用,也就是一個程序的資源各個線程都可以共享。就好比公司的飲水機,是屬于公司所有不屬于某一個線程所有,一個員工表示一個線程的話,那麼每個員工都可以去飲水機打水。

include<stdlib.h>
include<Windows.h>
void main()
{
     MessageBoxA(0,''ABC",''QWE",0);//彈出一個對話框
     MessageBoxA(0,''ABC",''QWE",0);
     MessageBoxA(0,''ABC",''QWE",0);
     MessageBoxA(0,''ABC",''QWE",0);
     MessageBoxA(0,''ABC",''QWE",0);
}

           

上面一段代碼,目的是彈出5個對話框,沒有做任何特殊的處理。我們運作這段代碼就是運作一個程序,且是單線程的。先彈出一個對話框,點選确定再彈出第二個…以此類推一共彈出5個。

從代碼分析多線程工作原理

現在我們用多線程并發來實作,同時彈出5個對話框的功能。也就是說我們需要5個線程來完成這個任務,一次性給它搞定。

include<stdlib.h>
include<process> //多線程頭檔案
include<Windows.h>

void run(void *p)//參數為一個空指針,可以指向任何類型
{
     MessageBoxA(0,''ABC",''QWE",0);//彈出一個對話框
}
void main()
{
     for(int i = 0;i<5;i++)
     {
         _beginthread(run,0,NULL);//使用一個for循環啟動5個線程
         //第一個參數為線程要執行任務的函數指針
         //第二個參數為線程棧的大小,0表示使用預設值,即一個棧的預設大小為1M
         //這個大小值是由編譯器維護了,如果需要更大的棧空間,我們可以手動修改。
         //第三個參數表示任務函數的傳入參數。
     }
     
}
           

運作結果如圖所示:

從代碼分析多線程工作原理

我們成功的使用多線程并發一次性完成5個視窗的彈出。通過系統工具檢視一下該程序與線程的狀态:

從代碼分析多線程工作原理

藍色為我們該程式的程序,我們看到該程序裡面有6個線程,莫慌,從上往下第三個線程為程序的主線程,它負責建立其他的子線程。就是這樣子。

我們來進階一下多線程

include<stdio.h>
include<stdlib.h>
include<process>

void gogo(void *p)
{
    int i = 0;
    while(1)//啟動一個while循環,每次循環間隔1秒,i自加一次,i>10時結束目前線程
    {
        if(i>10)
        {
            printf("%d",i);
            _endthread();//結束目前線程,與beginthread()正好相反
        }
        i++
        Sleep(1000);
    }
}
void time(void *p)
{
    int i = 0;
    while(1)
    {
        char str[100] = {0};//定義一個字元數組,長度100,初始化為0
        sprintf(str,"title 目前時間第%d",i);//将字元向str輸出,即将字元寫入到str
        i++;
        system(str);//系統執行,改變黑框的标題,顯示出時間
        Sleep(1000);//每次循環間隔一秒鐘
    }
}
void main()
{
     _beginthread(time,0,NULL);//同樣啟動多線程來執行time函數
     for(int i = 0;i<5;i++)
     {
          _beginthread(gogo,0,NULL);
          Sleep(1000);//延遲1秒建立一個線程
     }
}
           

花兩分鐘就閱讀完上面代碼,我們要的事情就是兩件,啟動一個子線程執行time函數。for循環啟動5個子線程,執行gogo函數。time函數負責在黑框标題顯示時間,gogo函數在運作到第11秒的時候結束自己目前的線程并且列印i的值,那麼就是有5個gogo函數被執行,列印,結束。main函數裡面延遲1秒建立一個線程,是以結束線程的時候就是一秒鐘結束一個。

從代碼分析多線程工作原理

如圖所示,我們可以看出多線程直接是并發的,且他們的執行互相不影響,各自己做自己的事情即可。如果我們想讓自己建立的多線程不并發,即串行執行。也就是說多個線程,一個執行完再到下一個線程成開始執行,以此類推。我們隻需要修改main函數裡面for循環的代碼如下

for(int i = 0;i<5;i++)
     {
          HANDLE hd = _beginthread(gogo,0,NULL);//使用hd接收開啟線程的傳回值為線程的編号其實是一個整數類型,HANDLE也是一種資料類型。
          WaitForSingleObject(hd,INFINITE);//阻塞線程,等待上一個線程執行完畢再開始下一個線程
          Sleep(1000);//延遲1秒建立一個線程
     }
           

是以我們知道多線程的排程有兩中模式,一種并發,一種串行。

我們可以使用多線程并發來解決很多問題,如在一個大資料下查找一個數。将資料集分成10份,給10個線程并發查找。這樣豈不是快很多。當其中一線程找到之後還可以通知其他還在找的線程不用再找了,我已經找到了。

下回分解。