C++11多線程知識點總結

一、多線程的基本概念

1、進程與線程的區別和聯繫

  • 進程:進程是一個動態的過程,是一個活動的實體。簡單來說,一個應用程序的運行就可以被看做是一個進程;

  • 線程:是運行中的實際的任務執行者。可以說,進程中包含了多個可以同時運行的線程。通俗理解:例如你打開微信就是打開一個進程,在微信裏面和好友視頻聊天就是開啓了一條線程。

  • 兩者之間的關係:一個進程裏面可以有多個線程,至少有一個線程。一個線程一定會在一個進程裏面。

2、併發,並行的區別

  • 併發同一時間段內交替運行多個進程(線程)

  • 並行同一時刻運行多個進程(線程)。很明顯,只有多處理器才能支持。

  併發就像我們的大腦思考一樣,同一個時刻只能想一件事,但是在很短的一個時間段內我們可以三心二意。當然如果你長了幾個腦袋,那你就可以並行思考了。

3、同步與異步,阻塞與非阻塞方式

  下面介紹同步,異步,阻塞,非阻塞這幾個概念,加深對多線程編程的理解。
  有了之前的概念,我們可以想象,當幾個線程或者進程在併發執行時,如果我們不加任何干預措施,那麼他們的執行順序是由系統當時的環境來決定的,所以不同時間段不同環境下運行的順序都會不盡相同,這便是異步(有差異的步驟)。當然,同步肯定就是通過一定的措施,使得幾個線程或者進程總是按照一定順序來執行(總是按照相同的步驟)。
  當一個進程或者線程請求某一個資源而不得時,如I/O,便會進入阻塞狀態,一直等待。scanf()便是一個很好的例子,當程序運行到scanf()時,如果輸入緩存區爲空,那麼程序便會進入阻塞狀態等待我們從鍵盤輸入,這便是以阻塞的方式調用scanf()。通過一定方法,我們可以將scanf()變成非阻塞的方式來執行。如給scanf()設置一個超時時間,如果時間到了還是沒有輸入那麼便跳過scanf(),這個時候我們就稱爲用非阻塞的方式來調用scanf()。
  對比可以發現,同步即阻塞。想要按照某特定順序來執行一系列過程,在上一個過程完成之前下一個過程必須等待,這就是阻塞在了這個地方。當同步運行的時候,會等待同步操作完成纔會返回,否則會一直阻塞在同步操作處。
  相反的,異步即非阻塞,當異步調用某個函數時,函數會立刻返回,而不會阻塞在那。

  怎麼判斷異步操作是否已經完成?通常有3種方式:

1. 狀態:異步操作完成時會將某個全局變量置爲特定值,可以通過輪詢判斷變量的值以確定是否操作完成;

2. 通知:異步操作完成會給調用者發送特定信號;

3. 回調:異步操作完成時會調用回調函數。

  所以同步即阻塞,異步即非阻塞。

4、線程阻塞的常見情況

1. 調用sleep()進入睡眠狀態;

2. 用wait()暫停了線程,除非收到notify()喚醒線程;

3. 線程正在等待一些IO操作;

4. 線程正在試圖調用被鎖起來了的對象。

二、線程的幾種狀態轉換

  線程在一定條件下,狀態會發生變化。線程一共有以下幾種狀態:

1. 新建狀態(New):新創建了一個線程對象;

2. 就緒狀態(Runnable):線程對象創建後,其他線程調用了該對象的start()方法。該狀態的線程位於“可運行線程池”中,變得可運行,只等待獲取CPU的使用權。即在就緒狀態的進程除CPU之外,其它的運行所需資源都已全部獲得;

3. 運行狀態(Running):就緒狀態的線程獲取了CPU,執行程序
代碼;

4. 阻塞狀態(Blocked):阻塞狀態是線程因爲某種原因放棄CPU使用權,暫時停止運行。直到線程進入就緒狀態,纔有機會轉到運行狀態。

5. 死亡狀態(dead):線程執行完了或者因異常退出了run()方法,該線程結束生命週期。

這裏寫圖片描述

三、多線程與單線程

1、多線程與單線程的區別

  單線程,顧名思義即是隻有一條線程在執行任務,這種情況在我們日常的工作學習中很少遇到,所以我們只是簡單做一下了解。

  多線程,創建多條線程同時執行任務,這種方式在我們的日常生活中比較常見。但是,在多線程的使用過程中,還有許多需要我們瞭解的概念。比如,在理解上並行和併發的區別,以及在實際應用的過程中多線程的安全問題,對此,我們需要進行詳細的瞭解。

  多線程和傳統的單線程在程序設計上最大的區別在於,由於各個線程的控制流彼此獨立,使得各個線程之間的代碼是亂序執行的,由此帶來的線程調度,同步等問題

2、多線程是否一定比單線程效率高?

  一提到多線程一般大家的第一感覺就是可以提升程序性能,在實際的操作中往往遇到性能的問題,都嘗試使用多線程來解決問題,但多線程程序並不是在任何情況下都能提升效率,在一些情況下恰恰相反,反而會降低程序的性能。對於單核CPU計算密集型任務,多線程反而並不能帶來效率的提升。線程本身由於創建和切換的開銷,採用多線程不會提高程序的執行速度,反而會降低速度。但是對於頻繁IO操作的程序,多線程可以有效的併發對於包含不同任務的程序,可以考慮每個任務使用一個線程。這樣的程序在設計上相對於單線程做所有事的程序來說,更爲清晰明瞭,比如生產、消費者問題。

  在實際的開發中對於性能優化的問題需要考慮到具體的場景來考慮是否使用多線程技術。

四、爲什麼要使用多線程

1、什麼時候使用多線程?

  線程必然不是越多越好,線程切換也是要開銷的,當你增加一個線程的時候,增加的額外開銷要小於該線程能夠消除的阻塞時間,這才叫物有所值。

  什麼時候該使用多線程呢?這要分四種情況討論:

1. 多核CPU——計算密集型任務。此時要儘量使用多線程,可以提高任務執行效率,例如加密解密,數據壓縮解壓縮(視頻、音頻、普通數據),否則只能使一個核心滿載,而其他核心閒置;

2. 單核CPU——計算密集型任務。此時的任務已經把CPU資源100%消耗了,就沒必要也不可能使用多線程來提高計算效率了;相反,如果要做人機交互,最好還是要用多線程,避免用戶沒法對計算機進行操作;

3. 單核CPU——IO密集型任務,使用多線程還是爲了人機交互方便;

4. 多核CPU——IO密集型任務,這就更不用說了,跟單核時候原因一樣。

2、什麼時候不使用多線程?

  知道什麼情況下不使用併發同樣重要。從根本上來說,不使用併發的唯一原因就是併發帶來的效益小於它帶來的代價。在許多情況下,使用併發會使代碼難以理解,編寫、維護併發代碼需要更多的腦力成本,併發帶來的複雜性可能會增加bug。除非併發帶來性能的提升足夠打,或者模塊劃分足夠清楚,否則不要使用併發。

  使用併發帶來性能上的提升可能不如預期。併發編程也需要額外的開銷,在創建一個線程時,系統要分配內核資源、棧空間,然後把新線程加入到任務隊列。如果線程運行時間小於線程的創建時間,這時使用多線程可能會使性能變差。

  進一步來說,線程資源是有限的。如果同時有太多線程,會佔用太多系統資源,會使整個系統變慢。使用太多線程會消耗盡內存或處理器的地址空間,因爲線程需要獨立的棧空間。

  在C/S架構下,如果爲每個連接創建一個線程,在連接比較少時性能很好,但是如果要同時處理太多連接的話會創建太多線程。這時使用線程池(thread pools)可以提高性能。在Linux下可以使用I/O多路複用:select、poll、epoll。

  線程的切換也需要時間,如果線程切換時間比線程運行時間還短,就會降低整體性能。

五、多線程優缺點

1、優點

1. 提高CPU的使用率:例如朋友圈發表圖片,當你上傳9張圖片的時候,如果開啓一個線程用同步的方式一張張上傳圖片,假設每次上傳圖片的線程只佔用了CPU 1%d的資源,剩下的99%資源就浪費了。但是如果你開啓9個線程同時上傳圖片,CPU就可以使用9%的資源了;

2. 提高程序的工作效率:還是拿朋友圈發表圖片來說,假設開啓一個線程上傳一張圖片的時間是1秒,那麼同步的方式上傳9張就需要9秒,但是你開啓9個線程同時上傳圖片,那麼就只需要1秒就完成了。

2、缺點

1. 如果有大量的線程,會影響性能,因爲CPU需要在它們之間切換

2. 更多的線程需要更多的內存空間

3. 多線程操作可能會出現線程安全或者死鎖等問題。

六、線程安全及解決方法

  線程安全:簡單的來說,就是在多個線程訪問一個類的時候,該類始終保持着正確的執行行爲。

1、線程安全出現的根本原因

1. 存在兩個或者兩個以上的線程對象共享同一個資源;

2. 多線程操作共享資源代碼有多個語句。

2、線程安全的解決方法

1. 無狀態對象永遠是線程安全的;

2. 原子性與競爭條件:原子性,顧名思義,指的就是不可分割的操作,多個操作要麼一起執行,要麼就都不執行。原子性保證了程序的執行不會因爲執行的時序問題而引發的線程安全問題。而經常引起原子性問題的就是競爭條件。比如常見的檢查再運行(check-then-act),當我創建一個文件夾時,會先判斷文件夾是否存在,不存在再創建。這個在單線程的情況下不會出現問題,但是在多線程下,就可能會因爲當我檢查文件夾不存在後,另一個線程先創建了該文件夾,從而導致此線程創建文件錯誤。

參考:https://blog.csdn.net/AC_hell/article/details/53613940
https://blog.csdn.net/sinat_36042530/article/details/52565296
https://blog.csdn.net/weixin_40271838/article/details/79998327
https://blog.csdn.net/SpadgerZ/article/details/52801087
https://blog.csdn.net/delacroix_xu/article/details/5928121
https://blog.csdn.net/H176Nhx7/article/details/78589949
https://www.cnblogs.com/lntea/p/4681730.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章