qileilove

          blog已經轉移至github,大家請訪問 http://qaseven.github.io/

          多線程的那點兒事(基礎篇)

          多線程編程是現代軟件技術中很重要的一個環節。要弄懂多線程,這就要牽涉到多進程?當然,要了解到多進程,就要涉及到操作系統。不過大家也不要緊張,聽我慢慢道來。這其中的環節其實并不復雜。

            (1)單CPU下的多線程

            在沒有出現多核CPU之前,我們的計算資源是唯一的。如果系統中有多個任務要處理的話,那么就需要按照某種規則依次調度這些任務進行處理。什么規則呢?可以是一些簡單的調度方法,比如說

            1)按照優先級調度

            2)按照FIFO調度

            3)按照時間片調度等等

            當然,除了CPU資源之外,系統中還有一些其他的資源需要共享,比如說內存、文件、端口、socket等。既然前面說到系統中的資源是有限的,那么獲取這些資源的最小單元體是什么呢,其實就是進程。

            舉個例子來說,在linux上面每一個享有資源的個體稱為task_struct,實際上和我們說的進程是一樣的。我們可以看看task_struct(linux 0.11代碼)都包括哪些內容

        1. struct task_struct {  
        2. /* these are hardcoded - don't touch */  
        3.     long state; /* -1 unrunnable, 0 runnable, >0 stopped */  
        4.     long counter;  
        5.     long priority;  
        6.     long signal;  
        7.     struct sigaction sigaction[32];  
        8.     long blocked;   /* bitmap of masked signals */  
        9. /* various fields */  
        10.     int exit_code;  
        11.     unsigned long start_code,end_code,end_data,brk,start_stack;  
        12.     long pid,father,pgrp,session,leader;  
        13.     unsigned short uid,euid,suid;  
        14.     unsigned short gid,egid,sgid;  
        15.     long alarm;  
        16.     long utime,stime,cutime,cstime,start_time;  
        17.     unsigned short used_math;  
        18. /* file system info */  
        19.     int tty;        /* -1 if no tty, so it must be signed */  
        20.     unsigned short umask;  
        21.     struct m_inode * pwd;  
        22.     struct m_inode * root;  
        23.     struct m_inode * executable;  
        24.     unsigned long close_on_exec;  
        25.     struct file * filp[NR_OPEN];  
        26. /* ldt for this task 0 - zero 1 - cs 2 - ds&ss */  
        27.     struct desc_struct ldt[3];  
        28. /* tss for this task */  
        29.     struct tss_struct tss;  
        30. };
        31.   每一個task都有自己的pid,在系統中資源的分配都是按照pid進行處理的。這也就說明,進程確實是資源分配的主體。

            這時候,可能有朋友會問了,既然task_struct是資源分配的主體,那為什么又出來thread?為什么系統調度的時候是按照thread調度,而不是按照進程調度呢?原因其實很簡單,進程之間的數據溝通非常麻煩,因為我們之所以把這些進程分開,不正是希望它們之間不要相互影響嘛。

            假設是兩個進程之間數據傳輸,那么需要如果需要對共享數據進行訪問需要哪些步驟呢

            1)創建共享內存

            2)訪問共享內存->系統調用->讀取數據

            3)寫入共享內存->系統調用->寫入數據

           要是寫個代碼,大家可能就更明白了

        32. #include <unistd.h>   
        33. #include <stdio.h>   
        34.   
        35. int value = 10;  
        36.   
        37. int main(int argc, char* argv[])  
        38. {  
        39.     int pid = fork();  
        40.     if(!pid){  
        41.         Value = 12;  
        42.         return 0;  
        43.     }  
        44.     printf("value = %d\n", value);  
        45.     return 1;  
        46. }
        47.   上面的代碼是一個創建子進程的代碼,我們發現打印的value數值還是10。盡管中間創建了子進程,修改了value的數值,但是我們發現打印下來的數值并沒有發生改變,這就說明了不同的進程之間內存上是不共享的。

            那么,如果修改成thread有什么好處呢?其實最大的好處就是每個thread除了享受單獨cpu調度的機會,還能共享每個進程下的所有資源。要是調度的單位是進程,那么每個進程只能干一件事情,但是進程之間是需要相互交互數據的,而進程之間的數據都需要系統調用才能應用,這在無形之中就降低了數據的處理效率。

            (2)多核CPU下的多線程

            沒有出現多核之前,我們的CPU實際上是按照某種規則對線程依次進行調度的。在某一個特定的時刻,CPU執行的還是某一個特定的線程。然而,現在有了多核CPU,一切變得不一樣了,因為在某一時刻很有可能確實是n個任務在n個核上運行。我們可以編寫一個簡單的open mp測試一下,如果還是一個核,運行的時間就應該是一樣的。

        48. #include <omp.h>   
        49. #define MAX_VALUE 10000000   
        50.   
        51. double _test(int value)  
        52. {  
        53.     int index;  
        54.     double result;  
        55.   
        56.     result = 0.0;  
        57.     for(index = value + 1; index < MAX_VALUE; index +=2 )  
        58.         result += 1.0 / index;  
        59.   
        60.     return result;  
        61. }  
        62.   
        63. void test()  
        64. {  
        65.     int index;  
        66.     int time1;  
        67.     int time2;  
        68.     double value1,value2;  
        69.     double result[2];  
        70.   
        71.     time1 = 0;  
        72.     time2 = 0;  
        73.   
        74.     value1 = 0.0;  
        75.     time1 = GetTickCount();  
        76.     for(index = 1; index < MAX_VALUE; index ++)  
        77.         value1 += 1.0 / index;  
        78.   
        79.     time1 = GetTickCount() - time1;  
        80.   
        81.     value2 = 0.0;  
        82.     memset(result , 0, sizeof(double) * 2);  
        83.     time2 = GetTickCount();  
        84.   
        85. #pragma omp parallel for   
        86.     for(index = 0; index < 2; index++)  
        87.         result[index] = _test(index);  
        88.   
        89.     value2 = result[0] + result[1];  
        90.     time2 = GetTickCount() - time2;  
        91.   
        92.     printf("time1 = %d,time2 = %d\n",time1,time2);  
        93.     return;  
        94. }
        95.   (3)多線程編程

            為什么要多線程編程呢?這其中的原因很多,我們可以舉例解決

            1)有的是為了提高運行的速度,比如多核cpu下的多線程

            2)有的是為了提高資源的利用率,比如在網絡環境下下載資源時,時延常常很高,我們可以通過不同的thread從不同的地方獲取資源,這樣可以提高效率

            3)有的為了提供更好的服務,比如說是服務器

            4)其他需要多線程編程的地方等等

          posted on 2011-12-08 14:28 順其自然EVO 閱讀(174) 評論(0)  編輯  收藏


          只有注冊用戶登錄后才能發表評論。


          網站導航:
           
          <2011年12月>
          27282930123
          45678910
          11121314151617
          18192021222324
          25262728293031
          1234567

          導航

          統計

          常用鏈接

          留言簿(55)

          隨筆分類

          隨筆檔案

          文章分類

          文章檔案

          搜索

          最新評論

          閱讀排行榜

          評論排行榜

          主站蜘蛛池模板: 镇宁| 和田县| 军事| 汾阳市| 广宗县| 隆尧县| 文登市| 大余县| 绥德县| 石河子市| 东至县| 谷城县| 汉川市| 弥渡县| 靖远县| 房产| 明水县| 浑源县| 沁源县| 弥渡县| 高州市| 林口县| 晋江市| 奎屯市| 云南省| 金乡县| 辽阳县| 定西市| 哈尔滨市| 安龙县| 河北省| 花垣县| 山丹县| 左权县| 桐庐县| 巫山县| 乐山市| 裕民县| 安西县| 罗城| 惠安县|