日本黄色一级经典视频|伊人久久精品视频|亚洲黄色色周成人视频九九九|av免费网址黄色小短片|黄色Av无码亚洲成年人|亚洲1区2区3区无码|真人黄片免费观看|无码一级小说欧美日免费三级|日韩中文字幕91在线看|精品久久久无码中文字幕边打电话

當前位置:首頁 > 單片機 > CPP開發(fā)者
[導讀]↓推薦關注↓為什么要并發(fā)編程大型的軟件項目常常包含非常多的任務需要處理。例如:對于大量數(shù)據(jù)的數(shù)據(jù)流處理,或者是包含復雜GUI界面的應用程序。如果將所有的任務都以串行的方式執(zhí)行,則整個系統(tǒng)的效率將會非常低下,應用程序的用戶體驗會非常的差。另一方面,自上個世紀六七十年代英特爾創(chuàng)始人之...


推薦關注↓


為什么要并發(fā)編程


大型的軟件項目常常包含非常多的任務需要處理。例如:對于大量數(shù)據(jù)的數(shù)據(jù)流處理,或者是包含復雜GUI界面的應用程序。如果將所有的任務都以串行的方式執(zhí)行,則整個系統(tǒng)的效率將會非常低下,應用程序的用戶體驗會非常的差。
另一方面,自上個世紀六七十年代英特爾創(chuàng)始人之一 Gordon Moore 提出?摩爾定義?以來,CPU頻率以每18個月翻一番的指數(shù)速度增長。但這一增長在最近的十年已經(jīng)基本停滯,大家會發(fā)現(xiàn)曾經(jīng)有過一段時間CPU的頻率從3G到達4G,但在這之后就停滯不前了。因此最近的新款CPU也基本上都是3G左右的頻率。相應的,CPU以更多核的形式在增長。目前的Intel i7有8核的版本,Xeon處理器達到了28核。并且,最近幾年手機上使用的CPU也基本上是4核或者8核的了。
由此,掌握并發(fā)編程技術,利用多處理器來提升軟件項目的性能將是軟件工程師的一項基本技能。
本文以C 語言為例,講解如何進行并發(fā)編程。并盡可能涉及C 11,C 14以及C 17中的主要內(nèi)容。

并發(fā)與并行


并發(fā)(Concurrent)與并行(Parallel)都是很常見的術語。
Erlang之父Joe Armstrong曾經(jīng)以人們使用咖啡機的場景為例描述了這兩個術語。如下圖所示:

  • 并發(fā):如果多個隊列可以交替使用某臺咖啡機,則這一行為就是并發(fā)的。
  • 并行:如果存在多臺咖啡機可以被多個隊列交替使用,則就是并行。

這里隊列中的每個人類比于計算機的任務,咖啡機類比于計算機處理器。因此:并發(fā)和并行都是在多任務的環(huán)境下的討論。
更嚴格的來說:如果一個系統(tǒng)支持多個動作同時存在,那么這個系統(tǒng)就是一個并發(fā)系統(tǒng)。如果這個系統(tǒng)還支持多個動作(物理時間上)同時執(zhí)行,那么這個系統(tǒng)就是一個并行系統(tǒng)。
你可能已經(jīng)看出,“并行”其實是“并發(fā)”的子集。它們的區(qū)別在于是否具有多個處理器。如果存在多個處理器同時執(zhí)行多個線程,就是并行。
在不考慮處理器數(shù)量的情況下,我們統(tǒng)稱之為“并發(fā)”。

進程與線程


進程與線程是操作系統(tǒng)的基本概念。無論是桌面系統(tǒng):MacOS,Linux,Windows,還是移動操作系統(tǒng):Android,iOS,都存在進程和線程的概念。
進程(英語:process),是指計算機中已運行的程序。進程為曾經(jīng)是分時系統(tǒng)的基本運作單位。在面向進程設計的系統(tǒng)(如早期的UNIX,Linux 2.4及更早的版本)中,進程是程序的基本執(zhí)行實體;線程(英語:thread)是操作系統(tǒng)能夠進行運算調度的最小單位。它被包含在進程之中,是進程中的實際運作單位。-- 維基百科

關于這兩個概念在任何一本操作系統(tǒng)書上都可以找到定義。網(wǎng)上也有很多文章對它們進行了解釋。因此這里不再贅述,這里僅僅提及一下它們與編程的關系。
對于絕大部分編程語言或者編程環(huán)境來說,我們所寫的程序都會在一個進程中運行。一個進程至少會包含一個線程。這個線程我們通常稱之為主線程。
在默認的情況下,我們寫的代碼都是在進程的主線程中運行,除非開發(fā)者在程序中創(chuàng)建了新的線程。
不同編程語言的線程環(huán)境會不一樣,Java語言在很早就支持了多線程接口。(Java程序在Java虛擬機中運行,虛擬機通常還會包含自己特有的線程,例如垃圾回收線程。)。而對于JavaScript這樣的語言來說,它就沒有多線程的概念。
當我們只有一個處理器時,所有的進程或線程會分時占用這個處理器。但如果系統(tǒng)中存在多個處理器時,則就可能有多個任務并行的運行在不同的處理器上。
下面兩幅圖以不同顏色的矩形代表不同的任務(可能是進程,也可能是線程)來描述它們可能在處理器上執(zhí)行的順序。
下圖是單核處理器的情況:

下面是四核處理器的情況:

任務會在何時占有處理器,通常是由操作系統(tǒng)的調度策略決定的。在《Android系統(tǒng)上的進程管理:進程的調度》一文中,我們介紹過Linux的調度策略。
當我們在開發(fā)跨平臺的軟件時,我們不應當對調度策略做任何假設,而應該抱有“系統(tǒng)可能以任意順序來調度我的任務”這樣的想法。

并發(fā)系統(tǒng)的性能


開發(fā)并發(fā)系統(tǒng)最主要的動機就是提升系統(tǒng)性能(事實上,這是以增加復雜度為代價的)。
但我們需要知道,單純的使用多線程并不一定能提升系統(tǒng)性能(當然,也并非線程越多系統(tǒng)的性能就越好)。從上面的兩幅圖我們就可以直觀的感受到:線程(任務)的數(shù)量要根據(jù)具體的處理器數(shù)量來決定。假設只有一個處理器,那么劃分太多線程可能會適得其反。因為很多時間都花在任務切換上了。
因此,在設計并發(fā)系統(tǒng)之前,一方面我們需要做好對于硬件性能的了解,另一方面需要對我們的任務有足夠的認識。
關于這一點,你可能需要了解一下阿姆達爾定律了。對于這個定律,簡單來說:我們想要預先意識到那些任務是可以并行的,那些是無法并行的。只有明確了任務的性質,才能有的放矢的進行優(yōu)化。這個定律告訴了我們將系統(tǒng)并行之后性能收益的上限。
關于阿姆達爾定律在Linux系統(tǒng)監(jiān)測工具sysstat介紹一文中已經(jīng)介紹過,因此這里不再贅述。

C 與并發(fā)編程


前面我們已經(jīng)了解到,并非所有的語言都提供了多線程的環(huán)境。
即便是C 語言,直到C 11標準之前,也是沒有多線程支持的。在這種情況下,Linux/Unix平臺下的開發(fā)者通常會使用POSIX Threads,Windows上的開發(fā)者也會有相應的接口。但很明顯,這些API都只針對特定的操作系統(tǒng)平臺,可移植性較差。如果要同時支持Linux和Windows系統(tǒng),你可能要寫兩套代碼。
相較而言,Java自JDK 1.0就包含了多線程模型。

這個狀態(tài)在C 11標準發(fā)布之后得到了改變。并且,在C 14和C 17標準中又對并發(fā)編程機制進行了增強。
下圖是最近幾個版本的C 標準特性的線路圖。

編譯器與C 標準


編譯器對于語言特性的支持是逐步完成的。想要使用特定的特性你需要相應版本的編譯器。
  • GCC對于C 特性的支持請參見這里:C Standards Support in GCC。
  • Clang對于C 特性的支持請參見這里:C Support in Clang。

下面兩個表格列出了C 標準和相應編譯器的版本對照:
  • C 標準與相應的GCC版本要求如下:
  • C 標準與相應的Clang版本要求如下:

默認情況下編譯器是以較低的標準來進行編譯的,如果希望使用新的標準,你需要通過編譯參數(shù)-std=c xx告知編譯器,例如:
g -std=c 17 your_file.cpp -o your_program

測試環(huán)境


本文的源碼可以到下載我的github上獲取,地址:paulQuei/cpp-concurrency。你可以直接通過下面這條命令獲取源碼:
git clone https://github.com/paulQuei/cpp-concurrency.git
源碼下載之后,你可以通過任何文本編輯器瀏覽源碼。如果希望編譯和運行程序,你還需要按照下面的內(nèi)容來準備環(huán)境。
本文中的源碼使用cmake編譯,只有cmake 3.8以上的版本才支持C 17,所以你需要安裝這個或者更新版本的cmake。
另外,截止目前(2019年10月)為止,clang編譯器還不支持并行算法。
但是gcc-9是支持的。因此想要編譯和運行這部分代碼,你需要安裝gcc 9.0或更新的版本。并且,gcc-9還要依賴Intel Threading Building Blocks才能使用并行算法以及頭文件。
具體的安裝方法見下文。
具體編譯器對于C 特性支持的情況請參見這里:C compiler support。

安裝好之后運行根目錄下的下面這個命令即可:
./make_all.sh
它會完成所有的編譯工作。
本文的源碼在下面兩個環(huán)境中經(jīng)過測試,環(huán)境的準備方法如下。

MacOS


在Mac上,我使用brew工具安裝gcc以及tbb庫。
考慮到其他人與我的環(huán)境可能會有所差異,所以需要手動告知tbb庫的安裝路徑。讀者需要執(zhí)行下面這些命令來準備環(huán)境:

rew install gccbrew insbtall tbb
export tbb_path=/usr/local/Cellar/tbb/2019_U8/./make_all.sh
注意,請通過運行g -9命令以確認gcc的版本是否正確,如果版本較低,則需要通過brew命令將其升級到新版本:
brew upgrade gcc

Ubuntu


Ubuntu上,通過下面的命令安裝gcc-9。
sudo add-apt-repository ppa:ubuntu-toolchain-r/testsudo apt-get updatesudo apt install gcc-9 g -9
但安裝tbb庫就有些麻煩了。這是因為Ubuntu 16.04默認關聯(lián)的版本是較低的,直接安裝是無法使用的。我們需要安裝更新的版本。聯(lián)網(wǎng)安裝的方式步驟繁瑣,所以可以通過下載包的方式進行安裝,我已經(jīng)將這需要的兩個文件放到的這里:
  • libtbb2_2019~U8-1_amd64.deb
  • libtbb-dev_2019~U8-1_amd64.deb

如果需要,你可以下載后通過apt命令安裝即可:
sudo apt install ~/Downloads/libtbb2_2019~U8-1_amd64.deb sudo apt install ~/Downloads/libtbb-dev_2019~U8-1_amd64.deb

線程


創(chuàng)建線程


創(chuàng)建線程非常的簡單的,下面就是一個使用了多線程的Hello World示例:
// 01_hello_thread.cpp
#include #include // ①
using namespace std; // ②
void hello() { // ③ cout << "Hello World from new thread." << endl;}
int main() { thread t(hello); // ④ t.join(); // ⑤
return 0;}
對于這段代碼說明如下:
  1. 為了使用多線程的接口,我們需要#include 頭文件。
  2. 為了簡化聲明,本文中的代碼都將using namespace std;。
  3. 新建線程的入口是一個普通的函數(shù),它并沒有什么特別的地方。
  4. 創(chuàng)建線程的方式就是構造一個thread對象,并指定入口函數(shù)。與普通對象不一樣的是,此時編譯器便會為我們創(chuàng)建一個新的操作系統(tǒng)線程,并在新的線程中執(zhí)行我們的入口函數(shù)。
  5. 關于join函數(shù)在下文中講解。

thread可以和callable類型一起工作,因此如果你熟悉lambda表達式,你可以直接用它來寫線程的邏輯,像這樣:
// 02_lambda_thread.cpp
#include #include
using namespace std;
int main() { thread t([] { cout << "Hello World from lambda thread." << endl; });
t.join();
return 0;}
為了減少不必要的重復,若無必要,下文中的代碼將不貼出include指令以及using聲明。

當然,你可以傳遞參數(shù)給入口函數(shù),像下面這樣:
// 03_thread_argument.cpp
void hello(string name) { cout << "Welcome to " << name << endl;}
int main() { thread t(hello, "https://paul.pub"); t.join();
return 0;}
不過需要注意的是,參數(shù)是以拷貝的形式進行傳遞的。因此對于拷貝耗時的對象你可能需要傳遞指針或者引用類型作為參數(shù)。但是,如果是傳遞指針或者引用,你還需要考慮參數(shù)對象的生命周期。因為線程的運行長度很可能會超過參數(shù)的生命周期(見下文detach),這個時候如果線程還在訪問一個已經(jīng)被銷毀的對象就會出現(xiàn)問題。

join與detach


  • 主要API

一旦啟動線程之后,我們必須決定是要等待直接它結束(通過join),還是讓它獨立運行(通過detach),我們必須二者選其一。如果在thread對象銷毀的時候我們還沒有做決定,則thread對象在析構函數(shù)出將調用std::terminate()從而導致我們的進程異常退出。
請思考在上面的代碼示例中,thread對象在何時會銷毀。

需要注意的是:在我們做決定的時候,很可能線程已經(jīng)執(zhí)行完了(例如上面的示例中線程的邏輯僅僅是一句打印,執(zhí)行時間會很短)。新的線程創(chuàng)建之后,究竟是新的線程先執(zhí)行,還是當前線程的下一條語句先執(zhí)行這是不確定的,因為這是由操作系統(tǒng)的調度策略決定的。不過這不要緊,我們只要在thread對象銷毀前做決定即可。
  • join:調用此接口時,當前線程會一直阻塞,直到目標線程執(zhí)行完成(當然,很可能目標線程在此處調用之前就已經(jīng)執(zhí)行完成了,不過這不要緊)。因此,如果目標線程的任務非常耗時,你就要考慮好是否需要在主線程上等待它了,因此這很可能會導致主線程卡住。
  • detach:detach是讓目標線程成為守護線程(daemon threads)。一旦detach之后,目標線程將獨立執(zhí)行,即便其對應的thread對象銷毀也不影響線程的執(zhí)行。并且,你無法再與之通信。

對于這兩個接口,都必須是可執(zhí)行的線程才有意義。你可以通過joinable()接口查詢是否可以對它們進行join或者detach。

管理當前線程


  • 主要API

上面是一些在線程內(nèi)部使用的API,它們用來對當前線程做一些控制。
  • yield 通常用在自己的主要任務已經(jīng)完成的時候,此時希望讓出處理器給其他任務使用。
  • get_id 返回當前線程的id,可以以此來標識不同的線程。
  • sleep_for 是讓當前線程停止一段時間。
  • sleep_until 和sleep_for類似,但是是以具體的時間點為參數(shù)。這兩個API都以chrono API(由于篇幅所限,這里不展開這方面內(nèi)容)為基礎。

下面是一個代碼示例:
// 04_thread_self_manage.cpp
void print_time() { auto now = chrono::system_clock::now(); auto in_time_t = chrono::system_clock::to_time_t(now);
std::stringstream ss; ss << put_time(localtime(
本站聲明: 本文章由作者或相關機構授權發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點,本站亦不保證或承諾內(nèi)容真實性等。需要轉載請聯(lián)系該專欄作者,如若文章內(nèi)容侵犯您的權益,請及時聯(lián)系本站刪除。
換一批
延伸閱讀

LED驅動電源的輸入包括高壓工頻交流(即市電)、低壓直流、高壓直流、低壓高頻交流(如電子變壓器的輸出)等。

關鍵字: 驅動電源

在工業(yè)自動化蓬勃發(fā)展的當下,工業(yè)電機作為核心動力設備,其驅動電源的性能直接關系到整個系統(tǒng)的穩(wěn)定性和可靠性。其中,反電動勢抑制與過流保護是驅動電源設計中至關重要的兩個環(huán)節(jié),集成化方案的設計成為提升電機驅動性能的關鍵。

關鍵字: 工業(yè)電機 驅動電源

LED 驅動電源作為 LED 照明系統(tǒng)的 “心臟”,其穩(wěn)定性直接決定了整個照明設備的使用壽命。然而,在實際應用中,LED 驅動電源易損壞的問題卻十分常見,不僅增加了維護成本,還影響了用戶體驗。要解決這一問題,需從設計、生...

關鍵字: 驅動電源 照明系統(tǒng) 散熱

根據(jù)LED驅動電源的公式,電感內(nèi)電流波動大小和電感值成反比,輸出紋波和輸出電容值成反比。所以加大電感值和輸出電容值可以減小紋波。

關鍵字: LED 設計 驅動電源

電動汽車(EV)作為新能源汽車的重要代表,正逐漸成為全球汽車產(chǎn)業(yè)的重要發(fā)展方向。電動汽車的核心技術之一是電機驅動控制系統(tǒng),而絕緣柵雙極型晶體管(IGBT)作為電機驅動系統(tǒng)中的關鍵元件,其性能直接影響到電動汽車的動力性能和...

關鍵字: 電動汽車 新能源 驅動電源

在現(xiàn)代城市建設中,街道及停車場照明作為基礎設施的重要組成部分,其質量和效率直接關系到城市的公共安全、居民生活質量和能源利用效率。隨著科技的進步,高亮度白光發(fā)光二極管(LED)因其獨特的優(yōu)勢逐漸取代傳統(tǒng)光源,成為大功率區(qū)域...

關鍵字: 發(fā)光二極管 驅動電源 LED

LED通用照明設計工程師會遇到許多挑戰(zhàn),如功率密度、功率因數(shù)校正(PFC)、空間受限和可靠性等。

關鍵字: LED 驅動電源 功率因數(shù)校正

在LED照明技術日益普及的今天,LED驅動電源的電磁干擾(EMI)問題成為了一個不可忽視的挑戰(zhàn)。電磁干擾不僅會影響LED燈具的正常工作,還可能對周圍電子設備造成不利影響,甚至引發(fā)系統(tǒng)故障。因此,采取有效的硬件措施來解決L...

關鍵字: LED照明技術 電磁干擾 驅動電源

開關電源具有效率高的特性,而且開關電源的變壓器體積比串聯(lián)穩(wěn)壓型電源的要小得多,電源電路比較整潔,整機重量也有所下降,所以,現(xiàn)在的LED驅動電源

關鍵字: LED 驅動電源 開關電源

LED驅動電源是把電源供應轉換為特定的電壓電流以驅動LED發(fā)光的電壓轉換器,通常情況下:LED驅動電源的輸入包括高壓工頻交流(即市電)、低壓直流、高壓直流、低壓高頻交流(如電子變壓器的輸出)等。

關鍵字: LED 隧道燈 驅動電源
關閉