swoole前置基礎(chǔ)知識1——1.1多進程/多線程的概念

一.為何需要多進程(或者多線程),為何需要并發(fā)?

這個問題或許本身都不是個問題鱼的。但是對于沒有接觸過多進程編程的朋友來說,他們確實無法感受到并發(fā)的魅力以及必要性喊儡。

我想,只要你不是整天都寫那種int main()到底的代碼的人稻据,那么或多或少你會遇到代碼響應(yīng)不夠用的情況艾猜,也應(yīng)該有嘗過并發(fā)編程的甜頭。就像一個快餐點的服務(wù)員,既要在前臺接待客戶點 餐匆赃,又要接電話送外賣淤毛,沒有分身術(shù)肯定會忙得你焦頭爛額的。幸運的是確實有這么一種技術(shù)算柳,讓你可以像孫悟空一樣分身低淡,靈魂出竅,樂哉樂哉地輕松應(yīng)付一切狀 況,這就是多進程/線程技術(shù)瞬项。

并發(fā)技術(shù)查牌,就是可以讓你在同一時間同時執(zhí)行多條任務(wù)的技術(shù)。你的代碼將不僅僅是從上到下滥壕,從左到右這樣規(guī)規(guī)矩矩的一條線執(zhí)行。你可以一條線在main函數(shù)里跟你的客戶交流兽泣,另一條線绎橘,你早就把你外賣送到了其他客戶的手里。

所以唠倦,為何需要并發(fā)称鳞?因為我們需要更強大的功能,提供更多的服務(wù)稠鼻,所以并發(fā)冈止,必不可少。

二.多進程

什么是進程候齿。最直觀的就是一個個pid,官方的說法就:進程是程序在計算機上的一次執(zhí)行活動熙暴。說得簡單點,下面這段代碼執(zhí)行的時候

int main()? {??

????printf(”pid is %d/n”,getpid() );??

????return 0;??

}??

進入main函數(shù)慌盯,這就是一個進程周霉,進程pid會打印出來,然后運行到return亚皂,該函數(shù)就退出俱箱,然后由于該函數(shù)是該進程的唯一的一次執(zhí)行,所以return后灭必,該進程也會退出狞谱。

看看多進程。linux下創(chuàng)建子進程的調(diào)用是fork();

#include <unistd.h>??

#include <sys/types.h>? ?

#include <stdio.h>??

void print_exit()? {??

????????printf("the exit pid:%d/n",getpid() );??

}??

main ()? ?{? ?

????????pid_t pid;? ?

????????atexit( print_exit );? ?? ?//注冊該進程退出時的回調(diào)函數(shù)??

????????pid=fork();? ?

? ?? ???if (pid < 0)? ?

? ?? ?? ?? ?? ? printf("error in fork!");? ?

? ?? ???else if (pid == 0)? ?

? ?? ?? ?? ?? ? printf("i am the child process, my process id is %d/n",getpid());? ?

????????else? {??

????????????????printf("i am the parent process, my process id is %d/n",getpid());? ?

????????????????sleep(2);??

????????????????wait();??

? ?? ? }?

}?

這是gcc測試下的運行結(jié)果禁漓。

i am the child process, my process id is 15806

the exit pid:15806

i am the parent process, my process id is 15805

the exit pid:15805

關(guān)于fork函數(shù)跟衅,功能就是產(chǎn)生子進程,由于前面說過璃饱,進程就是執(zhí)行的流程活動与斤。

那么fork產(chǎn)生子進程的表現(xiàn)就是它會返回2次,一次返回0,順序執(zhí)行下面的代碼撩穿。這是子進程磷支。

一次返回子進程的pid,也順序執(zhí)行下面的代碼食寡,這是父進程雾狈。

(為何父進程需要獲取子進程的pid呢?這個有很多原因抵皱,其中一個原因:看最后的wait善榛,就知道父進程等待子進程的終結(jié)后,處理其task_struct結(jié)構(gòu)呻畸,否則會產(chǎn)生僵尸進程,扯遠(yuǎn)了移盆,有興趣可以自己google)。

如果fork失敗伤为,會返回-1.

額外說下atexit(print_exit) —— 需要的參數(shù)肯定是函數(shù)的調(diào)用地址咒循。

這里的print_exit 是函數(shù)名還是函數(shù)指針呢?答案是函數(shù)指針绞愚,函數(shù)名永遠(yuǎn)都只是一串無用的字符串叙甸。

某本書上的規(guī)則:函數(shù)名在用于非函數(shù)調(diào)用的時候,都等效于函數(shù)指針位衩。

說到子進程只是一個額外的流程裆蒸,那他跟父進程的聯(lián)系和區(qū)別是什么呢?

我很想建議你看看linux內(nèi)核的注解(有興趣可以看看糖驴,那里才有本質(zhì)上的了解)僚祷,總之,fork后,子進程會復(fù)制父進程的task_struct結(jié)構(gòu)遂赠,并為子進程的堆棧分配物理頁久妆。理論上來說,子進程應(yīng)該完整地復(fù)制父進程的堆跷睦,棧以及數(shù)據(jù)空間筷弦,但是2者共享正文段。

關(guān)于寫時復(fù)制:由于一般 fork后面都接著exec抑诸,所以烂琴,現(xiàn)在的 fork都在用寫時復(fù)制的技術(shù),顧名思意蜕乡,就是奸绷,數(shù)據(jù)段,堆层玲,棧号醉,一開始并不復(fù)制反症,由父,子進程共享畔派,并將這些內(nèi)存設(shè)置為只讀铅碍。直到父,子進程一方嘗試寫 這些區(qū)域线椰,則內(nèi)核才為需要修改的那片內(nèi)存拷貝副本胞谈。這樣做可以提高 fork的效率。

三.多線程

線程是可執(zhí)行代碼的可分派單元憨愉。這個名稱來源于“執(zhí)行的線索”的概念烦绳。在基于線程的多任務(wù)的環(huán)境中,所有進程有至少一個線程配紫,但是它們可以具有多個任務(wù)径密。這意味著單個程序可以并發(fā)執(zhí)行兩個或者多個任務(wù)。

簡而言之躺孝,線程就是把一個進程分為很多片睹晒,每一片都可以是一個獨立的流程。這已經(jīng)明顯不同于多進程了括细,進程是一個拷貝的流程,而線程只是把一條河流截成很 多條小溪戚啥。它沒有拷貝這些額外的開銷奋单,但是僅僅是現(xiàn)存的一條河流,就被多線程技術(shù)幾乎無開銷地轉(zhuǎn)成很多條小流程猫十,它的偉大就在于它少之又少的系統(tǒng)開銷览濒。 (當(dāng)然偉大的后面又引發(fā)了重入性等種種問題,這個后面慢慢比較)拖云。

還是先看linux提供的多線程的系統(tǒng)調(diào)用:

int pthread_create(pthread_t *restrict tidp, const pthread_attr_t *restrict attr, void *(*start_rtn)(void), void *restrict arg);

Returns: 0 if OK, error number on failure

第一個參數(shù)為指向線程標(biāo)識符的指針贷笛。

第二個參數(shù)用來設(shè)置線程屬性。

第三個參數(shù)是線程運行函數(shù)的起始地址宙项。

最后一個參數(shù)是運行函數(shù)的參數(shù)乏苦。


#include<stdio.h>??

#include<string.h>??

#include<stdlib.h>??

#include<unistd.h>??

#include<pthread.h>?


void* task1(void*);??

void* task2(void*);?

void usr();??

int p1,p2;??


int main()? {??

? ? usr();?

? ? getchar();??

? ? return 1;??

}??


void usr()? {??

????????pthread_t pid1, pid2;??

????????pthread_attr_t attr;??

????????void *p;??

????????int ret=0;??

????????pthread_attr_init(&attr);? ?? ?? ?//初始化線程屬性結(jié)構(gòu)??

????????pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);? ?//設(shè)置attr結(jié)構(gòu)為分離??

????????pthread_create(&pid1, &attr, task1, NULL);? ? ?//創(chuàng)建線程,返回線程號給pid1,線程屬性設(shè)置為attr的屬性尤筐,線程函數(shù)入口為task1汇荐,參數(shù)為NULL??

????????pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);??

????????pthread_create(&pid2, &attr, task2, NULL);? //前臺工作??

????????ret=pthread_join(pid2, &p);? ?//等待pid2返回,返回值賦給p??

????????printf("after pthread2:ret=%d,p=%d/n", ret,(int)p);? ? ? ? ? ??

}??


void* task1(void *arg1)? {??

????????printf("task1/n");??

? ??????//艱苦而無法預(yù)料的工作盆繁,設(shè)置為分離線程掀淘,任其自生自滅??

? ? ? ? pthread_exit( (void *)1);??

}??

void* task2(void *arg2)? {??

????????int i=0;??

????????printf("thread2 begin./n");??

????????//繼續(xù)送外賣的工作??

????????pthread_exit((void *)2);??

}?

這個多線程的例子應(yīng)該很明了了,主線程做自己的事情油昂,生成2個子線程革娄,task1為分離倾贰,任其自生自滅,而task2還是繼續(xù)送外賣拦惋,需要等待返回匆浙。(因該還記得前面說過僵尸進程吧,線程也是需要等待的架忌。如果不想等待吞彤,就設(shè)置線程為分離線程)

額外的說下,linux下要編譯使用線程的代碼叹放,一定要記得調(diào)用pthread庫饰恕。如下編譯:

gcc -o pthrea -pthread??pthrea.c

四.比較以及注意事項

1.看完前面,應(yīng)該對多進程和多線程有個直觀的認(rèn)識井仰。如果總結(jié)多進程和多線程的區(qū)別埋嵌,你肯定能說,前者開銷大俱恶,后者開銷較小雹嗦。確實,這就是最基本的區(qū)別合是。

2.線程函數(shù)的可重入性:

說到函數(shù)的可重入了罪,和線程安全,我偷懶了聪全,引用網(wǎng)上的一些總結(jié)泊藕。

線程安全:概念比較直觀。一般說來难礼,一個函數(shù)被稱為線程安全的娃圆,當(dāng)且僅當(dāng)被多個并發(fā)線程反復(fù)調(diào)用時,它會一直產(chǎn)生正確的結(jié)果蛾茉。

可重入:概念基本沒有比較正式的完整解釋讼呢,但是它比線程安全要求更嚴(yán)格。根據(jù)經(jīng)驗谦炬,所謂“重入”悦屏,常見的情況是,程序執(zhí)行到某個函數(shù)foo()時键思,收到信 號窜管,于是暫停目前正在執(zhí)行的函數(shù),轉(zhuǎn)到信號處理函數(shù)稚机,而這個信號處理函數(shù)的執(zhí)行過程中幕帆,又恰恰也會進入到剛剛執(zhí)行的函數(shù)foo(),這樣便發(fā)生了所謂的重 入赖条。此時如果foo()能夠正確的運行失乾,而且處理完成后常熙,之前暫停的foo()也能夠正確運行,則說明它是可重入的碱茁。

線程安全的條件:

要確保函數(shù)線程安全裸卫,主要需要考慮的是線程之間的共享變量。屬于同一進程的不同線程會共享進程內(nèi)存空間中的全局區(qū)和堆纽竣,而私有的線程空間則主要包括棧和寄 存器墓贿。因此,對于同一進程的不同線程來說蜓氨,每個線程的局部變量都是私有的聋袋,而全局變量、局部靜態(tài)變量穴吹、分配于堆的變量都是共享的幽勒。在對這些共享變量進行訪 問時,如果要保證線程安全港令,則必須通過加鎖的方式啥容。

可重入的判斷條件:

要確保函數(shù)可重入,需滿足一下幾個條件:

1顷霹、不在函數(shù)內(nèi)部使用靜態(tài)或全局?jǐn)?shù)據(jù)

2咪惠、不返回靜態(tài)或全局?jǐn)?shù)據(jù),所有數(shù)據(jù)都由函數(shù)的調(diào)用者提供淋淀。

3硝逢、使用本地數(shù)據(jù),或者通過制作全局?jǐn)?shù)據(jù)的本地拷貝來保護全局?jǐn)?shù)據(jù)绅喉。

4、不調(diào)用不可重入函數(shù)叫乌。

可重入與線程安全并不等同柴罐,一般說來,可重入的函數(shù)一定是線程安全的憨奸,但反過來不一定成立革屠。它們的關(guān)系可用下圖來表示:

比如:strtok函數(shù)是既不可重入的,也不是線程安全的排宰;加鎖的strtok不是可重入的似芝,但線程安全;而strtok_r既是可重入的板甘,也是線程安全的党瓮。

如果我們的線程函數(shù)不是線程安全的,那在多線程調(diào)用的情況下盐类,可能導(dǎo)致的后果是顯而易見的——共享變量的值由于不同線程的訪問寞奸,可能發(fā)生不可預(yù)料的變化呛谜,進而導(dǎo)致程序的錯誤,甚至崩潰枪萄。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末隐岛,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子瓷翻,更是在濱河造成了極大的恐慌聚凹,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,743評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件齐帚,死亡現(xiàn)場離奇詭異妒牙,居然都是意外死亡,警方通過查閱死者的電腦和手機童谒,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,296評論 3 385
  • 文/潘曉璐 我一進店門单旁,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人饥伊,你說我怎么就攤上這事象浑。” “怎么了琅豆?”我有些...
    開封第一講書人閱讀 157,285評論 0 348
  • 文/不壞的土叔 我叫張陵愉豺,是天一觀的道長。 經(jīng)常有香客問我茫因,道長蚪拦,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,485評論 1 283
  • 正文 為了忘掉前任冻押,我火速辦了婚禮驰贷,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘洛巢。我一直安慰自己括袒,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 65,581評論 6 386
  • 文/花漫 我一把揭開白布稿茉。 她就那樣靜靜地躺著锹锰,像睡著了一般。 火紅的嫁衣襯著肌膚如雪漓库。 梳的紋絲不亂的頭發(fā)上恃慧,一...
    開封第一講書人閱讀 49,821評論 1 290
  • 那天,我揣著相機與錄音渺蒿,去河邊找鬼痢士。 笑死,一個胖子當(dāng)著我的面吹牛茂装,可吹牛的內(nèi)容都是我干的良瞧。 我是一名探鬼主播陪汽,決...
    沈念sama閱讀 38,960評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼褥蚯!你這毒婦竟也來了挚冤?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,719評論 0 266
  • 序言:老撾萬榮一對情侶失蹤赞庶,失蹤者是張志新(化名)和其女友劉穎训挡,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體歧强,經(jīng)...
    沈念sama閱讀 44,186評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡澜薄,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,516評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了摊册。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片肤京。...
    茶點故事閱讀 38,650評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖茅特,靈堂內(nèi)的尸體忽然破棺而出忘分,到底是詐尸還是另有隱情,我是刑警寧澤白修,帶...
    沈念sama閱讀 34,329評論 4 330
  • 正文 年R本政府宣布妒峦,位于F島的核電站,受9級特大地震影響兵睛,放射性物質(zhì)發(fā)生泄漏肯骇。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,936評論 3 313
  • 文/蒙蒙 一祖很、第九天 我趴在偏房一處隱蔽的房頂上張望笛丙。 院中可真熱鬧,春花似錦假颇、人聲如沸胚鸯。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,757評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至啊终,卻和暖如春镜豹,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背蓝牲。 一陣腳步聲響...
    開封第一講書人閱讀 31,991評論 1 266
  • 我被黑心中介騙來泰國打工趟脂, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人例衍。 一個月前我還...
    沈念sama閱讀 46,370評論 2 360
  • 正文 我出身青樓昔期,卻偏偏與公主長得像已卸,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子硼一,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,527評論 2 349

推薦閱讀更多精彩內(nèi)容

  • Linux-創(chuàng)建進程與線程用到的函數(shù)解析 【1】exit: exit函數(shù)可以退出程序并將控制權(quán)返回給操作系統(tǒng)累澡,而用...
    Yojiaku閱讀 3,596評論 0 2
  • 1.內(nèi)存的頁面置換算法 (1)最佳置換算法(OPT)(理想置換算法):從主存中移出永遠(yuǎn)不再需要的頁面哼蛆;如無這樣的...
    杰倫哎呦哎呦閱讀 3,235評論 1 9
  • (1) 進程與線程區(qū)別蕊梧?進程是并發(fā)執(zhí)行的程序在執(zhí)行過程中分配和管理資源的基本單位,一個動態(tài)概概念腮介,是競爭計算機資源...
    Lee_Lemon閱讀 622評論 0 1
  • ### main函數(shù)執(zhí)行之前做了什么?(iOS) & dyld 是Apple 的動態(tài)鏈接器肥矢;在 xnu 內(nèi)核為程...
    天使君閱讀 680評論 0 1
  • 深痛悼念甘兄 天蒼穹,夜深沉叠洗。 徹夜難眠甘改,思緒萬千。 草木一秋人一世惕味。 忙忙碌碌苦一生楼誓。 兒孫滿堂享清福。 而卻撒...
    開心果_c632閱讀 367評論 0 0