国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

Java多線程筆記(零):進程、線程與通用概念

blastz / 1319人閱讀

摘要:父進程調用創建子進程。因而,一個進程的第一個線程會隨著這個進程的啟動而創建,這個線程被稱為該進程的主線程。另一方面,線程不可能獨立于進程存在。終止線程線程可以通過多種方式來終結同一個進程中的其他線程。

前言

不積跬步,無以至千里;不積小流,無以成江海。在學習Java多線程相關的知識前,我們首先需要去了解一點操作系統的進程、線程以及相關的基礎概念。

進程

通常,我們把一個程序的執行稱為一個進程。反過來講,進程用于描述程序的執行過程。因此,程序和進程是一對概念,它們分別描述了一個程序的靜態和動態特征:除此之外,進程還操作系統進行資源分配的一個基本單位。

進程的衍生

進程使用fork系統調用來創建。父進程調用fork創建子進程。每個子進程都是源自它的父進程的一個副本,它會獲得父進程的數據段、堆和棧的副本,并與父進程共享代碼段。每一份副本都是獨立的,子進程對屬于它的副本的修改對其父進程和兄弟進程(同父進程)都是不可見的,反之亦然。全盤復制父進程的數據是一種相當低效的做法。 Linux操作系統內核使用寫時復制(Copy on Write,常簡稱為COW)等技術來提高進程創建的效率。當然,剛創建的子進程也可以通過系統調用exec把一個新的程序加載到己的內存中,而原先在內存中的數據段、堆、棧以及代碼段就會被替換掉,在這之后,子進程執行的就會是那個剛剛加載進來的新程序。

父進程被如果優先于子進程結束,那么子進程就會被原來父進程的父進程“收養”。

為了管理進程,內核必須對每個進程的數據和行為進行詳細的記錄,包括進程的優先級、狀態、虛擬地址范圍以及各種訪問權限等等。更具體地說,這些信息都會被記在每個進程的進程描述符中。進程描述符并不是一個簡單的符號,而是一個非常復雜的數據結構。保存在進程描述符中的進程ID (常稱為PID )是進程在操作系統中的唯一標識,其中進程ID為1的進程就是之前提到的內核啟動進程。進程id是一個非負整數且總是順序的編號,新創建的進程ID總是前一個進程ID遞增的結果。此外,進程ID也可以重復使用。當進程ID達到其最大限值時,內核會從頭開始查找閑置的進程ID并使用M先找到的那一個作為新進程的ID。另外,進程描述符中還會包含當前進程的父進程的ID (常稱為PPID )。

進程間的同步

如果多個進程之間需要協作完成任務,那么進程間通信的方式就是需要重點考慮的事項之一。這種通信叫做IPC(Inter-Process Communication)。那么在Linux中,從處理機制的角度看,可以分為三大類方法:

基于通信的IPC

基于信號的IPC

基于同步的IPC

通信IPC

以數據為傳送手段的IPC

管道(pipe):用于傳輸字節流

消息隊列(message queue):用來傳輸結構化的對象

以共享內存為手段的IPC

共享內存區(share memory):最快的IPC方法

信號IPC

操作系統的信號(signal)機制:唯一一種異步IPC方法。通過kill -l查看。

同步IPC

信號量(semaphore)

進程的狀態

在Linux中,每個進程在每個時刻只會有一種狀態,分別有以下六種

可運行狀態(TASK_RUNNING)

該進程立刻或正在CPU上運行。但是運行的時期是不確定的,由進程調度來決定。

可中斷的睡眠狀態(TASK_INTERRUPTABLE)

如果一個進程正在等待某個事件到來時,會進入此狀態。這樣的進程會被放入對應的等待隊列中。當事件發生時,對應的等待隊列中的一個或多個進程就會被喚醒。

不可中斷的睡眠狀態(TASK_UNINTERRUPTIBLE)

此種狀態可與中斷的睡眠狀態的唯一區別是它不可被打斷。這意味著此種狀態的進程不會對任何信號作出響應。更確切地講,發送給此狀態的進程的信號直到它狀態轉出才會被傳遞過去。處于此狀態的進程通常是在等待一個特殊的時間,比如等待同步的IO操作完成。

暫停狀態(TASK_STOPPED或TASK_TRACED)或跟蹤狀態

向進程發送SIGSTOP信號,就會使該進程轉入暫停狀態,除非該進程正處于不可中斷的睡眠狀態。

向正處于暫停的進程發送SIGCONT信號,會使用該進程轉向可運行狀態。處于該狀態的進程會暫停,并等待另一個進程(跟蹤它的那個進程)對它進行操作。例如,我們使用調試工具GDB在某個程序中設置一個斷點,而后對應的進程運行到該斷點處就會停下來。這時,該進程就處于跟蹤狀態。跟蹤狀態與暫停狀態非常類似。但是,向處于跟蹤狀態的進程發送SIGCONT信號并不能使它回復。只有當調試進程進行了相應的系統調用或退出后,它才能夠恢復。

僵尸狀態(TASK_DEAD-EXIT_ZOMBIE)

處于此狀態的進程即將結束運行,該進程占用的絕大多數資源也都已經被回收,不過還有一些信息未還是拿出,比如退出碼以及一些統計信息。之所以保留這些信息,主要是考慮到該進程的父進程可能需要它們。由于此時的進程主體已經被刪除而只留下一個空殼,故此狀態才被稱為僵尸狀態。

退出狀態(TASK_DEAD-EXIT_DEAD)

在進程退出的過程中,有可能連退出碼和統計信息都不需要保留。造成這種情況的原因可能是顯示地讓該進程的父進程忽略掉SIGCHLD信號(當一個進程消亡的時候,內核會給其父進程發送SIGCHLD信號以告知此情況),也可能是該進程已經被分離(分離即讓子進程和父進程分別獨立運行)。分離后的子程序將不會再使用和執行與父進程共享代碼段中的指令,而是加載并運行一個全新的程序。在這些情況下,該進程處于退出的時候就不會轉入僵尸狀態,而會直接轉入退出狀態。處于退出狀態的進程會立即被干凈利落地結束掉,它占用的系統資源也會被操作系統自動回收。

內核為每個用戶進程分配的是虛擬內存而不是物理內存。同時,內核會把進程的虛擬內存劃分為若干頁(page),而物理內存單元的劃分由CPU負責。一個物理內存單元被稱為一個頁框(page freame)。不同進程的大多數頁都會與不同的頁框相對應。對應的時候那就是共享內存了。

線程

線程可以視為進程中的控制流。一個進程至少包含一個線程,因為其他至少會有一個控制流持續運行。因而,一個進程的第一個線程會隨著這個進程的啟動而創建,這個線程被稱為該進程的主線程。當然,一個進程可以包含多個線程。這些線程都是由當前線程中已經存在的線程創建出來的,創建的方法就是調用系統調用(pthread_create)。擁有多個線程的進程可以并發執行多個任務,并且即時某個或某些任務被阻塞,也不會影響其他任務執行,這可以大大改善程序的響應時間和吞吐量。另一方面,線程不可能獨立于進程存在。它的生命周期不可能逾越所屬進程的生命周期。

一個進程中的所有線程都擁有自己線程棧,并以此存儲自己的私有數據。這些線程的線程棧都包含在其所屬進程的虛擬內存地址中。不過要注意,一個進程中的很多資源都會被其中的所有線程共享,這些被線程共享的資源包含當前進程所持有文件描述符,等等。正因為如此,同一個進程的多個線程運行的一定是同一個程序,只不過具體的控制流程的執行函數可能有所不同。在同一個進程的多個線程之間共享數據也是一件非常輕松和自然的事情。另外,創建一個新線程,也不會像創建一個新進程那樣耗時費力,因為在其所屬進程的虛擬內存地址中存儲的代碼、數據和資源都不需要被復制。

另外,操作系統和提供了一定的系統調用用于管理當前進程中的線程。

線程的標識

和進程一樣,每個線程都有自己的ID(由內核分配),叫做線程ID或者TID。但是在操作系統范圍內不唯一,在所屬進程的范圍內唯一。

線程的控制

任何一個線程都可以同一線程中的其他線程進行有限管理,如下:

創建線程

主線程在其所屬進程啟動時創建。其他線程可以通過別的線程用pthread_create來創建——要傳入新線程將要執行的函數以及傳入該函數的參數值。在創建成功的時候,該函數會返回線程的TID。

終止線程

線程可以通過多種方式來終結同一個進程中的其他線程。其他一種方式就是調用系統調用pthread_cancel,其作用是取消掉給定線程ID代表的那個線程。更確切地講,它會向目標線程發送一個請求,要求它立刻終止執行。但是該函數只是發送請求并即可返回。但是,該函數只是發送請求并立刻返回,而不會等待目標線程對該請求做出響應。至于目標線程什么時候對此做出線程、怎么樣的響應,則取決與另外的因素(比如線程目標的取消狀態及類型)。在默認情況下,目標線程總是會接受線程取消請求,不過等到時機成熟(執行到某個取消點)的時候,目標線程才會響應線程的取消請求。

連接已終止的線程

此操作由系統調用pthread_join來執行,該函數會一直等待與給定的線程ID對應的那個線程終止,并把線程執行的pthread_create函數的返回值告知調用線程。如果目標線程已經處于終止狀態,那么該函數會立即返回。這就像是把調用線程放置在了目標線程的后面,當目標線程把線程控制權交出時,調用線程會接過流程控制權并繼續執行pthread_join函數調用之后的代碼。這也把這一操作稱為連接的緣由之一。實際上,如果一個線程可被連接,那么在它終止之前就必須連接,否則就會變成一個僵尸線程。僵尸線程不但會導致系統資源浪費,還會無意義減少其進程的可創建線程數量。

分離線程

將一個線程分離后那么它將變得不可連接。而在默認情況下,一個線程總是可以被連接的。分離操作的另一個作用是讓操作系統內核在目標線程終止時自行進行清理和銷毀工作。注意,分離操作是不可逆的。也就是說,我們無法使一個不可連接的線程變回可連接的狀態。不過,對于一個已處于分離狀態的線程,執行終止操作仍然會起作用。分離操作由系統調用pthread_detach來執行,它接受一個代表了線程ID的參數值。

一個線程對自身也可以進行兩種控制:終止和分離。線程終止自身的方式有很多種。在線程執行的start函數中執行return語句,會使該線程隨著start函數的結束而終止。需要注意的是,如果在主線程中執行了return語句,那么當前進程中的所有線程都會終止。另外,在任意線程中調用系統調用exit也會達到這種效果。還有一種終止自身的方式就是顯示調用pthread_exit。

而分離pthread_detach函數則是傳入自己的TID。

多線程與多進程

在多個線程之間交換線程是非常簡單和自然的事,而在多個進程之間只能通過一些額外的手段(比如管道、消息隊列、信號量和共享內存區)傳遞數據。顯然,使用這些額外手段會增加開發成本。不過,線程間交換數據雖然簡單但卻由于可能發生競態條件而不得不使用一些同步工具(比如互斥量和條件變量)加以保護。這些與業務邏輯無關的代碼會增加程序的復雜度,尤其在使用不當的情況下還會引起災難。

互斥量可以理解為我們常見的鎖。而條件變量所做的就是保證線程間共享的數據狀態改變時通知到其他因此而被阻塞的線程。條件變量總是與互斥量組合使用。當線程成功鎖定互斥量并訪問到共享數據時,共享數據的狀態并不一定滿足它的要求。下面就通過一個示例來描述條件變量的使用場景。

通用概念 原子操作

執行過程不能中斷的操作稱為原子操作(atomic operation)。必須一個單一的匯編指令表示,而且需要得到芯片級別的支持。

臨界區

臨界區(critical section)用來表示一種公共資源或者共享數據,可以被多個線程使用。但是每一次,只有一個線程可以使它,一旦臨界區資源被占用,其他線程要想使用資源,就必須等待,即串行化訪問或執行。

互斥

保證只有一個進程或線程在臨界區內的做法只有一個——互斥(mutual exclusion。簡稱 mutex)。

同步和異步

描述的是用戶線程與內核的交互方式:

同步(Synchrounous)是指用戶線程發起 I/O 請求后需要等待或者輪詢內核 I/O 操作完成后才能繼續執行;

異步(Asynchrounous)是指用戶線程發起 I/O 請求后仍繼續執行,當內核 I/O 操作完成后會通知用戶線程,或者調用用戶線程注冊的回調函數。

阻塞和非阻塞

描述的是用戶線程調用內核 I/O 操作的方式:

阻塞(Blocking)是指 I/O 操作需要徹底完成后才返回到用戶空間;

非阻塞(Non-Blocking)是指 I/O 操作被調用后立即返回給用戶一個狀態值,無需等到 I/O 操作徹底完成。

一個 I/O 操作其實分成了兩個步驟:

發起 I/O 請求

實際的 I/O 操作。

阻塞 I/O 和非阻塞 I/O 的區別在于第一步,發起 I/O 請求是否會被阻塞。如果阻塞直到完成那么就是傳統的阻塞 I/O ,如果不阻塞,那么就是非阻塞 I/O 。 同步 I/O 和異步 I/O 的區別就在于第二個步驟是否阻塞,如果實際的 I/O 讀寫阻塞請求進程,那么就是同步 I/O 。

并發(Concurrency)和并行(Parallelism)

并發和并行往往被人所混淆。它們都可以表示兩個或多個任務一起執行,但是偏重點有些不同。并發偏重于多個任務交替執行,而多個任務有可能還是串行。而并行則是真正意義上的“同時執行”。

嚴格來說,并行的多個任務是真實的同時執行,而對并發來說,這個過程這是交替的,一會兒運行任務A一會兒執行任務B,系統會不停地在兩者間切換。但對于外部觀察者來說,即使多個任務之間是串行并發的,也會造成多任務間是并行執行的錯覺。

死鎖(DeadLock)、饑餓(Starvation)和活鎖(Livelock)

死鎖、饑餓和活鎖都屬于多線程的活躍性問題,如果發生上述情況,那么相關線程可能就不再活躍,也就是說它可能很難繼續往下執行了。

死鎖應該是最糟糕的一種情況了,雖然別的情況也沒有好到哪兒去。

死鎖:多個線程互相等待多方釋放資源而一直沒有執行。

饑餓:一個或多個線程因為種種原因無法獲取所得的需要資源,導致一直無法執行。導致的原因往往是當前線程優先級不高導致沒有資源,或某線程一直占著關鍵資源不放。

活鎖:多個線程都釋放資源給別的線程使用,導致沒有線程拿到資源而正常執行。

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/66071.html

相關文章

  • 線程三分鐘就可以入個門了!

    摘要:系統級線程核心級線程由操作系統內核進行管理。值得注意的是多線程的存在,不是提高程序的執行速度。實現多線程上面說了一大堆基礎,理解完的話。虛擬機的啟動是單線程的還是多線程的是多線程的。 前言 之前花了一個星期回顧了Java集合: Collection總覽 List集合就這么簡單【源碼剖析】 Map集合、散列表、紅黑樹介紹 HashMap就是這么簡單【源碼剖析】 LinkedHashMa...

    awkj 評論0 收藏0
  • Python

    摘要:最近看前端都展開了幾場而我大知乎最熱語言還沒有相關。有關書籍的介紹,大部分截取自是官方介紹。但從開始,標準庫為我們提供了模塊,它提供了和兩個類,實現了對和的進一步抽象,對編寫線程池進程池提供了直接的支持。 《流暢的python》閱讀筆記 《流暢的python》是一本適合python進階的書, 里面介紹的基本都是高級的python用法. 對于初學python的人來說, 基礎大概也就夠用了...

    dailybird 評論0 收藏0
  • 如何準備校招技術面試

    摘要:網易跨境電商考拉海購在線筆試現場技術面面。如何看待校招面試招聘,對公司而言,是尋找勞動力對員工而言,是尋找未來的同事。 如何準備校招技術面試 標簽 : 面試 [TOC] 2017 年互聯網校招已近尾聲,作為一個非 CS 專業的應屆生,零 ACM 經驗、零期刊論文發表,我通過自己的努力和準備,從找實習到校招一路運氣不錯,面試全部通過,謹以此文記錄我的校招感悟。 寫在前面 寫作動機 ...

    MkkHou 評論0 收藏0

發表評論

0條評論

blastz

|高級講師

TA的文章

閱讀更多
最新活動
閱讀需要支付1元查看
<