多線(xiàn)程編程是現代軟件技術(shù)中很重要的一個(gè)環(huán)節。要弄懂多線(xiàn)程,這就要牽涉到多進(jìn)程?當然,要了解到多進(jìn)程,就要涉及到操作系統。不過(guò)大家也不要緊張,聽(tīng)我慢慢道來(lái)。這其中的環(huán)節其實(shí)并不復雜。 (1)單CPU下的多線(xiàn)程 在沒(méi)有出現多核CPU之前,我們的計算資源是唯一的。如果系統中有多個(gè)任務(wù)要處理的話(huà),那么就需要按照某種規則依次調度這些任務(wù)進(jìn)行處理。什么規則呢?可以是一些簡(jiǎn)單的調度方法,比如說(shuō) “ 1)按照優(yōu)先級調度 2)按照FIFO調度 3)按照時(shí)間片調度等等 當然,除了CPU資源之外,系統中還有一些其他的資源需要共享,比如說(shuō)內存、文件、端口、socket等。既然前面說(shuō)到系統中的資源是有限的,那么獲取這些資源的最小單元體是什么呢,其實(shí)就是進(jìn)程。 舉個(gè)例子來(lái)說(shuō),在Linux上面每一個(gè)享有資源的個(gè)體稱(chēng)為task_struct,實(shí)際上和我們說(shuō)的進(jìn)程是一樣的。我們可以看看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. }; 每一個(gè)task都有自己的pid,在系統中資源的分配都是按照pid進(jìn)行處理的。這也就說(shuō)明,進(jìn)程確實(shí)是資源分配的主體。 這時(shí)候,可能有朋友會(huì )問(wèn)了,既然task_struct是資源分配的主體,那為什么又出來(lái)thread?為什么系統調度的時(shí)候是按照thread調度,而不是按照進(jìn)程調度呢?原因其實(shí)很簡(jiǎn)單,進(jìn)程之間的數據溝通非常麻煩,因為我們之所以把這些進(jìn)程分開(kāi),不正是希望它們之間不要相互影響嘛。 假設是兩個(gè)進(jìn)程之間數據傳輸,那么需要如果需要對共享數據進(jìn)行訪(fǎng)問(wèn)需要哪些步驟呢 “ 1)創(chuàng )建共享內存 2)訪(fǎng)問(wèn)共享內存->系統調用->讀取數據 3)寫(xiě)入共享內存->系統調用->寫(xiě)入數據 要是寫(xiě)個(gè)代碼,大家可能就更明白了, 1. #include 2. #include 3. 4. int value = 10; 5. 6. int main(int argc, char* argv[]) 7. { 8. int pid = fork(); 9. if(!pid){ 10. Value = 12; 11. return 0; 12. } 13. printf("value = %d\n", value); 14. return 1; 15. } 上面的代碼是一個(gè)創(chuàng )建子進(jìn)程的代碼,我們發(fā)現打印的value數值還是10。盡管中間創(chuàng )建了子進(jìn)程,修改了value的數值,但是我們發(fā)現打印下來(lái)的數值并沒(méi)有發(fā)生改變,這就說(shuō)明了不同的進(jìn)程之間內存上是不共享的。 那么,如果修改成thread有什么好處呢?其實(shí)最大的好處就是每個(gè)thread除了享受單獨cpu調度的機會(huì ),還能共享每個(gè)進(jìn)程下的所有資源。要是調度的單位是進(jìn)程,那么每個(gè)進(jìn)程只能干一件事情,但是進(jìn)程之間是需要相互交互數據的,而進(jìn)程之間的數據都需要系統調用才能應用,這在無(wú)形之中就降低了數據的處理效率。 (2)多核CPU下的多線(xiàn)程 沒(méi)有出現多核之前,我們的CPU實(shí)際上是按照某種規則對線(xiàn)程依次進(jìn)行調度的。在某一個(gè)特定的時(shí)刻,CPU執行的還是某一個(gè)特定的線(xiàn)程。然而,現在有了多核CPU,一切變得不一樣了,因為在某一時(shí)刻很有可能確實(shí)是n個(gè)任務(wù)在n個(gè)核上運行。我們可以編寫(xiě)一個(gè)簡(jiǎn)單的open mp測試一下,如果還是一個(gè)核,運行的時(shí)間就應該是一樣的。 1. #include 2. #define MAX_VALUE 10000000 3. 4. double _test(int value) 5. { 6. int index; 7. double result; 8. 9. result = 0.0; 10. for(index = value + 1; index < MAX_VALUE; index +=2 ) 11. result += 1.0 / index; 12. 13. return result; 14. } 15. 16. void test() 17. { 18. int index; 19. int time1; 20. int time2; 21. double value1,value2; 22. double result[2]; 23. 24. time1 = 0; 25. time2 = 0; 26. 27. value1 = 0.0; 28. time1 = GetTickCount(); 29. for(index = 1; index < MAX_VALUE; index ++) 30. value1 += 1.0 / index; 31. 32. time1 = GetTickCount() - time1; 33. 34. value2 = 0.0; 35. memset(result , 0, sizeof(double) * 2); 36. time2 = GetTickCount(); 37. 38. #pragma omp parallel for 39. for(index = 0; index < 2; index++) 40. result[index] = _test(index); 41. 42. value2 = result[0] + result[1]; 43. time2 = GetTickCount() - time2; 44. 45. printf("time1 = %d,time2 = %d\n",time1,time2); 46. return; 47. } (3)多線(xiàn)程編程 為什么要多線(xiàn)程編程呢?這其中的原因很多,我們可以舉例解決 1)有的是為了提高運行的速度,比如多核cpu下的多線(xiàn)程 2)有的是為了提高資源的利用率,比如在網(wǎng)絡(luò )環(huán)境下下載資源時(shí),時(shí)延常常很高,我們可以通過(guò)不同的thread從不同的地方獲取資源,這樣可以提高效率 3)有的為了提供更好的服務(wù),比如說(shuō)是服務(wù)器 4)其他需要多線(xiàn)程編程的地方等等 ![]() |