多線程編程是現代軟件技術中很重要的一個環(huán)節(jié)。要弄懂多線程,這就要牽涉到多進程?當然,要了解到多進程,就要涉及到操作系統(tǒng)。不過大家也不要緊張,聽我慢慢道來。這其中的環(huán)節(jié)其實并不復雜。
(1)單CPU下的多線程
在沒有出現多核CPU之前,我們的計算資源是唯一的。如果系統(tǒng)中有多個任務要處理的話,那么就需要按照某種規(guī)則依次調度這些任務進行處理。什么規(guī)則呢?可以是一些簡單的調度方法,比如說
1)按照優(yōu)先級調度
2)按照FIFO調度
3)按照時間片調度等等
當然,除了CPU資源之外,系統(tǒng)中還有一些其他的資源需要共享,比如說內存、文件、端口、socket等。既然前面說到系統(tǒng)中的資源是有限的,那么獲取這些資源的最小單元體是什么呢,其實就是進程。
舉個例子來說,在linux上面每一個享有資源的個體稱為task_struct,實際上和我們說的進程是一樣的。我們可以看看task_struct(linux 0.11代碼)都包括哪些內容
struct task_struct { /* these are hardcoded - don't touch */ long state; /* -1 unrunnable, 0 runnable, >0 stopped */ long counter; long priority; long signal; struct sigaction sigaction[32]; long blocked; /* bitmap of masked signals */ /* various fields */ int exit_code; unsigned long start_code,end_code,end_data,brk,start_stack; long pid,father,pgrp,session,leader; unsigned short uid,euid,suid; unsigned short gid,egid,sgid; long alarm; long utime,stime,cutime,cstime,start_time; unsigned short used_math; /* file system info */ int tty; /* -1 if no tty, so it must be signed */ unsigned short umask; struct m_inode * pwd; struct m_inode * root; struct m_inode * executable; unsigned long close_on_exec; struct file * filp[NR_OPEN]; /* ldt for this task 0 - zero 1 - cs 2 - ds&ss */ struct desc_struct ldt[3]; /* tss for this task */ struct tss_struct tss; }; |
每一個task都有自己的pid,在系統(tǒng)中資源的分配都是按照pid進行處理的。這也就說明,進程確實是資源分配的主體。
這時候,可能有朋友會問了,既然task_struct是資源分配的主體,那為什么又出來thread?為什么系統(tǒng)調度的時候是按照thread調度,而不是按照進程調度呢?原因其實很簡單,進程之間的數據溝通非常麻煩,因為我們之所以把這些進程分開,不正是希望它們之間不要相互影響嘛。
假設是兩個進程之間數據傳輸,那么需要如果需要對共享數據進行訪問需要哪些步驟呢
1)創(chuàng)建共享內存
2)訪問共享內存->系統(tǒng)調用->讀取數據
3)寫入共享內存->系統(tǒng)調用->寫入數據
要是寫個代碼,大家可能就更明白了
#include <unistd.h> #include <stdio.h> int value = 10; int main(int argc, char* argv[]) { int pid = fork(); if(!pid){ Value = 12; return 0; } printf("value = %d\n", value); return 1; } |
上面的代碼是一個創(chuàng)建子進程的代碼,我們發(fā)現打印的value數值還是10。盡管中間創(chuàng)建了子進程,修改了value的數值,但是我們發(fā)現打印下來的數值并沒有發(fā)生改變,這就說明了不同的進程之間內存上是不共享的。
那么,如果修改成thread有什么好處呢?其實最大的好處就是每個thread除了享受單獨cpu調度的機會,還能共享每個進程下的所有資源。要是調度的單位是進程,那么每個進程只能干一件事情,但是進程之間是需要相互交互數據的,而進程之間的數據都需要系統(tǒng)調用才能應用,這在無形之中就降低了數據的處理效率。
(2)多核CPU下的多線程
沒有出現多核之前,我們的CPU實際上是按照某種規(guī)則對線程依次進行調度的。在某一個特定的時刻,CPU執(zhí)行的還是某一個特定的線程。然而,現在有了多核CPU,一切變得不一樣了,因為在某一時刻很有可能確實是n個任務在n個核上運行。我們可以編寫一個簡單的open mp測試一下,如果還是一個核,運行的時間就應該是一樣的。
#include <omp.h> #define MAX_VALUE 10000000 double _test(int value) { int index; double result; result = 0.0; for(index = value + 1; index < MAX_VALUE; index +=2 ) result += 1.0 / index; return result; } void test() { int index; int time1; int time2; double value1,value2; double result[2]; time1 = 0; time2 = 0; value1 = 0.0; time1 = GetTickCount(); for(index = 1; index < MAX_VALUE; index ++) value1 += 1.0 / index; time1 = GetTickCount() - time1; value2 = 0.0; memset(result , 0, sizeof(double) * 2); time2 = GetTickCount(); #pragma omp parallel for for(index = 0; index < 2; index++) result[index] = _test(index); value2 = result[0] + result[1]; time2 = GetTickCount() - time2; printf("time1 = %d,time2 = %d\n",time1,time2); return; } |
(3)多線程編程
為什么要多線程編程呢?這其中的原因很多,我們可以舉例解決
1)有的是為了提高運行的速度,比如多核cpu下的多線程
2)有的是為了提高資源的利用率,比如在網絡環(huán)境下下載資源時,時延常常很高,我們可以通過不同的thread從不同的地方獲取資源,這樣可以提高效率
3)有的為了提供更好的服務,比如說是服務器
4)其他需要多線程編程的地方等等