日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Go学习笔记—多线程

發(fā)布時間:2023/12/10 编程问答 40 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Go学习笔记—多线程 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

多線程編程

? 一個進(jìn)程可以包含多個線程,這些線程運(yùn)行的一定是同一個程序(進(jìn)程==程序),且都由當(dāng)前進(jìn)程中已經(jīng)存在的線程通過系統(tǒng)調(diào)用的方式創(chuàng)建出來。進(jìn)程是資源分配的基本單位,線程是調(diào)度運(yùn)行的基本單位,線程不可獨(dú)立于進(jìn)程存在。

? 所有線程都有自己的線程棧,以此存放自己的私有數(shù)據(jù)(包含在進(jìn)程的虛擬內(nèi)存地址中)。一個進(jìn)程中的很多資源也會被線程所共享,包括在當(dāng)前進(jìn)程虛擬內(nèi)存地址中存放的代碼段、數(shù)據(jù)段、堆、信號處理函數(shù)、文件描述符(非負(fù)整數(shù))。正因如此,創(chuàng)建一個線程時不會像創(chuàng)建一個進(jìn)程時那樣耗費(fèi)資源,因?yàn)槠浯蠖鄶?shù)資源都因共享而無需被復(fù)制。

(一)線程簡介

一、線程狀態(tài)

? 與進(jìn)程這種家族式的樹狀結(jié)構(gòu)不同,線程之間的關(guān)系都是平等的。他們可以互相進(jìn)行如下四種操作。

操作作用
pthread_create創(chuàng)建線程——除主線程隨著進(jìn)程一同創(chuàng)建,其他線程都通過線程傳入代碼段與參數(shù)來創(chuàng)建,并會返回TID。
pthread_cancel(TID)終止線程——取消給定TID代表的線程,目標(biāo)線程總是會接受該請求并在某取消點(diǎn)響應(yīng)該請求。
pthread_join(TID)連接已終止線程——一直等待到該TID代表的線程終止,并將其返回值告知調(diào)用線程。
pthread_detach(TID)分離線程——將目標(biāo)線程變?yōu)椴豢杀贿B接。內(nèi)核會在其終止時自動進(jìn)行清理和銷毀工作。

注意:

(1)線程默認(rèn)都是一個可連接線程。如果在其終止時未被連接,則會變成一個僵尸線程,當(dāng)有其他進(jìn)程連接該僵尸進(jìn)程,則會被回收。

(2)分離操作是不可逆的操作。

(3)創(chuàng)建新城需要調(diào)用系統(tǒng)調(diào)用并給定執(zhí)行函數(shù)和參數(shù),線程可以有返回值。

(4)return、系統(tǒng)調(diào)用exit會終止當(dāng)前、所有線程。顯示調(diào)用pthread_exit也會終止線程。二者的區(qū)別是主線程調(diào)用前者會終止進(jìn)程內(nèi)所有線程,后者只會終止主線程。

二、線程調(diào)度

? 線程的執(zhí)行總是趨向于CPU受限或IO受限。而調(diào)度器會為IO受限的線程提供更高的動態(tài)優(yōu)先級。調(diào)度器會盡量使一個線程在一個特定的CPU上運(yùn)行,這樣可以提高cache命中率以及高效使用內(nèi)存,當(dāng)一個CPU過于繁忙時,調(diào)度器也將線程遷移至空閑CPU運(yùn)行。

名稱說明作用
動態(tài)優(yōu)先級可以被調(diào)度器實(shí)時調(diào)整(在靜態(tài)優(yōu)先級的基礎(chǔ)上得到)決定線程的運(yùn)行順序
靜態(tài)優(yōu)先級只可以由應(yīng)用程序指定,默認(rèn)為0決定CPU時間片的大小

? 線程會按照動態(tài)優(yōu)先級的大小排列在激活的優(yōu)先級陣列中。當(dāng)一個線程已經(jīng)占用了較長的CPU時間(T<=時間片大小),那么該線程就會被排入過期的優(yōu)先級陣列,在其后的線程則會被放在CPU上運(yùn)行。當(dāng)激活的陣列已經(jīng)沒有等待線程時,會將激活陣列與過期陣列進(jìn)行身份互換,繼續(xù)運(yùn)行新激活陣列上的線程。

? 線程會因?yàn)榈却硞€事件或條件的發(fā)生而加入到對應(yīng)的等待隊(duì)列中,并隨即進(jìn)入睡眠狀態(tài)。當(dāng)事件發(fā)生時,內(nèi)核會通知對應(yīng)的等待隊(duì)列中的所有線程,這些線程會被喚醒并被轉(zhuǎn)移到適當(dāng)?shù)倪\(yùn)行隊(duì)列中。

狀態(tài)名描述
可中斷的睡眠狀態(tài)睡眠直到某個條件變?yōu)檎?#xff0c;如產(chǎn)生一個硬件中斷、釋放正在等待的系統(tǒng)資源或是傳遞一個信號。
不可中斷的睡眠狀態(tài)只能被如硬件中斷、正在等待的系統(tǒng)資源被釋放等喚醒,對其他進(jìn)程傳遞的信號不響應(yīng)。

三、線程實(shí)現(xiàn)模型
  • 用戶級線程模型

? 線程由用戶級別的線程庫全程管理。這些線程存儲在進(jìn)程的用戶空間,內(nèi)核無法感知。線程的創(chuàng)建、終止、切換全部在用戶態(tài)下完成。然而由于線程無法被內(nèi)核調(diào)度,所以該進(jìn)程被調(diào)度器視為一個無法分割的整體單元,無法實(shí)現(xiàn)多線程并發(fā)以及CPU負(fù)載均衡。它實(shí)現(xiàn)的實(shí)際上是一個多用戶級線程對應(yīng)一個內(nèi)核調(diào)度實(shí)體(M:1)。

  • 內(nèi)核級線程模型——Linux

? 線程由內(nèi)核負(fù)責(zé)管理。應(yīng)用程序?qū)€程的創(chuàng)建、終止、同步都必須通過內(nèi)核提供的系統(tǒng)調(diào)用完成,所以操作系統(tǒng)無需在線程庫級別管理線程。然而內(nèi)核創(chuàng)建大量調(diào)度實(shí)體與線程對應(yīng),會用到更多的內(nèi)核資源,同時內(nèi)核的線程管理成本要比用戶級線程管理高很多,線程創(chuàng)建、切換、同步耗費(fèi)的時間也更高,給內(nèi)核調(diào)度器帶來巨大負(fù)擔(dān)。它實(shí)現(xiàn)的實(shí)際上是每一個用戶級線程對應(yīng)一個內(nèi)核調(diào)度實(shí)體(1:1)。

  • 兩級線程模型——Go

? 線程由內(nèi)核與線程庫共同管理。一個進(jìn)程可以關(guān)聯(lián)多個內(nèi)核調(diào)度實(shí)體,而進(jìn)程中的線程卻不與其一一對應(yīng),這些線程可以映射到同一個已關(guān)聯(lián)的內(nèi)核調(diào)度實(shí)體上。即先通過操作系統(tǒng)內(nèi)核創(chuàng)建多個內(nèi)核級線程,通過這些內(nèi)核級線程對用戶級線程進(jìn)行調(diào)度。它實(shí)現(xiàn)的實(shí)際上是多個用戶級線程對應(yīng)多個內(nèi)核調(diào)度實(shí)體(M:N)。Go中稱用戶級線程為goroutine。

四、線程同步

? 由于一個進(jìn)程所擁有的相當(dāng)一部分虛擬內(nèi)存地址都可以被該進(jìn)程中的所有線程共享,為保障共享數(shù)據(jù)的一致性,引入臨界區(qū)概念。即只能被串行化訪問或執(zhí)行的某個資源或代碼段。通過采用互斥量、原子操作這類同步工具保證臨界區(qū)有效。

  • 互斥量
  • ? 在同一時刻,只允許一個線程出于臨界區(qū)內(nèi)的約束稱為互斥。每個線程在進(jìn)入臨界區(qū)之前,都必須先鎖定某個對象,只有成功鎖定該對象的線程才可以進(jìn)入臨界區(qū),否則阻塞。這個對象就稱為互斥量。

    ? 互斥量分為初始化—(未鎖定狀態(tài))—鎖定—(已鎖定狀態(tài))—解鎖—(未鎖定狀態(tài))。每個互斥量保護(hù)的臨界區(qū)應(yīng)該在合理范圍內(nèi)并且盡可能的大,但如果多個線程頻繁出入較大臨界區(qū)并且發(fā)生沖突,則應(yīng)考慮切分該臨界區(qū)并使用不同互斥量加以保護(hù)。注意在不同互斥量保護(hù)的臨界區(qū)不應(yīng)該包含對同一個資源的同種(不同)操作。這將導(dǎo)致多個進(jìn)程可以通過不同的互斥量進(jìn)入同一臨界區(qū)。

    ? 互斥量的出現(xiàn)導(dǎo)致的唯一問題就是死鎖。一般通過“試鎖定-回退”或者“固定順序鎖定”解決。前者在鎖定多個鎖失敗時,會解鎖之前的互斥量,并重新嘗試加鎖;后者規(guī)定所有線程上鎖的順序,永遠(yuǎn)是鎖定了1才可以鎖定2。

  • 條件變量
  • ? 條件變量與互斥量組合使用。當(dāng)對應(yīng)的共享數(shù)據(jù)狀態(tài)發(fā)生變化時,通知其他因此而被阻塞的進(jìn)程。

    操作作用
    等待通知(wait)阻塞當(dāng)前線程,直至收到該條件變量發(fā)來的通知
    單發(fā)通知(signal)該條件變量向至少一個正在等待它通知的線程發(fā)送通知
    廣播通知(broadcast)該條件變量給正在等待它通知的所有線程發(fā)送通知

    ? 注意:(1)等待通知操作在臨界區(qū)內(nèi)進(jìn)行,即線程獲取到互斥量后,發(fā)現(xiàn)臨界區(qū)的值不符合條件,所以解鎖互斥量并阻塞該線程。

    ? (2)等待通知(解鎖互斥量,阻塞當(dāng)前線程)是個原子操作。阻塞之后該線程會一直等待條件變量通知,并嘗試再次鎖定。

    五、線程安全

    ? 線程安全:一個代碼塊,可以被多個線程并發(fā)執(zhí)行,并且總能達(dá)到預(yù)期效果,則認(rèn)為是線程安全。

    ? 可重入函數(shù):一個函數(shù),如果多個線程并發(fā)的調(diào)用的結(jié)果,和它們以任意順序依次調(diào)用的結(jié)果總是相同的,則認(rèn)為是可重入函數(shù)。如果一個函數(shù)把共享數(shù)據(jù)作為它返回的結(jié)果或者包含在它返回的結(jié)果中,它一定不是一個可重入函數(shù)。

    ? 程序性能指標(biāo):響應(yīng)時間和吞吐量。

    ? 程序的正確性和可伸縮性:后者是指增加CPU核心數(shù)的情況下,其運(yùn)行速度不會受到負(fù)面影響(在多CPU并行的條件下,實(shí)現(xiàn)互斥量之類的串行操作,需要內(nèi)核、CPU共同協(xié)調(diào),CPU核心越多,協(xié)調(diào)工作越復(fù)雜)。如何在平衡二者的關(guān)系?

    • 控制臨界區(qū)純度:臨界區(qū)代碼只包含操作共享數(shù)據(jù)的代碼
    • 控制臨界區(qū)粒度:粒度過細(xì)會增加底層協(xié)調(diào)工作的次數(shù),所以將幾段操作同一共享數(shù)據(jù)的臨界區(qū)合并。
    • 減少臨界區(qū)代碼執(zhí)行耗時:對一個包含操作不同共享數(shù)據(jù)的臨界區(qū),應(yīng)將其分為多個臨界區(qū);同時改進(jìn)算法。
    • 避免長時間持有互斥量:在臨界區(qū)中代碼會等待某個共享數(shù)據(jù)的情況,引入條件變量來適時對該互斥量進(jìn)行解鎖與鎖定。
    • 優(yōu)先使用原子操作而不是互斥量

    總結(jié)

    以上是生活随笔為你收集整理的Go学习笔记—多线程的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。