C++多線程學習之(一)——並發與多線程


1 並發

計算機領域的並發指的是在單個系統里同時執行多個獨立的任務,而非順序地進行一些活動。

1.1 並發的途徑

多進程並發:將應用程序分為多個獨立的進程,它們在同一時刻運行,就像同時進行網頁瀏覽和文字處理一樣。獨立的進程可以通過進程間常規的通信渠道傳遞訊息。

  • 缺點:操作系統會在進程間提供了一定的保護措施,以避免一個進程去修改另一個進程的數據。這導致進程之間的通信通常不是設置復雜,就是速度慢;

      運行多個進程需要時間啟動進程,操作系統需要內部資源來管理進程。

  • 優點:操作系統在進程間提供附加的保護操作和更高級別的通信機制,意味着可以更容易編寫安全的並發代碼

      可以使用遠程連接的方式,在不同的機器上運行獨立的進程。

多線程並發:並發的另一個途徑是在單個進程中運行多個線程。線程類似於輕量級的進程,每個線程相互獨立運行,且線程可以在不同的指令序列中運行。

  • 缺點:缺少線程間的數據保護,如果數據要被多個線程訪問,我們必須確保每個線程所訪問的的數據是一致的,因此需要對線程通信做大量的工作。
  • 優點:進程中所有線程共享地址空間,並且缺少數據保護,使得操作系統的記錄工作量減小,所以使用多線程的相關開銷遠遠小於使用多個進程;單一進程中的多線程間的通信開銷較小。

1.2 利用並發提高性能的方式

  • 任務並行(易並行):將一個單個任務分成幾部分,並且各自並行運行,從而降低總運行時間。此種方法具有良好的可擴展性,當可用硬件線程的數量增加時,算法的並行性也隨之增加。當可用硬件線程的數量增加時,算法的並行性也會隨之增加。如果算法中有不易並行的部分,你可以把算法划分成固定(不可擴展)數量的並行任務。
  • 數據並行(可並行):每個線程在不同的數據部分上執行相同的操作。處理一個數據塊仍然需要同樣的時間,但在相同的時間內處理了更多的數據。這種方法所帶來的吞吐量提升,可以讓某些新功能成為可能。

2 多線程示例

經典的例子:打印出Hello World。

單線程中運行Hello World程序:

1 #include <iostream> 
2 int main()
3 {
4     std::cout << "Hello World!" << std::endl;
5

利用多線程,啟動一個獨立的線程顯示Hello World:

 1 #include <iostream>
 2 #include <thread>
 3 void hello()
 4 {
 5     std::cout << "Hello World!" << std::endl;
 6 }
 7 
 8 int main()
 9 {
10     std::thread t(hello);
11     t.join();
12 }

 比較兩個程序,可以看出一些差別:首先,增加了頭文件 #include <thread> ,標准C++庫中對多線程支持的聲明在新的頭文件中,管理線程的函數和類在 <thread> 中聲明,而保護共享數據的函數和類在其它頭文件中聲明。其次,打印信息的代碼被移動到了一個獨立的函數中。因為每個線程都必須具有一個初始函數,新線程的執行從這里開始。對於應用程序而言,初始線程是 main() ,但是對於其它線程,可以在 std::thread 對象的構造函數中指定。除此之外,與直接寫入標准輸出或是從 main() 調用 hello() 不同,該程序啟動了一個全新的線程來實現,將線程數量一分為二——初始線程始於 main(),而新線程始於 hello()。新的線程啟動之后,初始線程繼續執行,如果它不等新線程結束,它就將自顧自地繼續運行到 main()的結束,從而結束程序,有可能發生在新線程運行之前。

本示例僅僅為了將一條信息寫入標准輸出而做了大量的工作,一般來說並不值得為了如此簡單的任務而使用多線程,尤其是在此期間初始線程並沒做什么。因為操作系統需要分配內核相關資源和堆棧空間,所以在啟動線程時存在固有的開銷,然后才能把新線程加入調度器中,所有這一切都需要時間。如果在線程上的任務完成得很快,那么任務實際執行的時間要比啟動線程的時間小很多,這就會導致應用程序的整體性能還不如直接使用“產生線程”的方式。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM