<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          C++ 多線(xiàn)程處理(編程)

          共 5871字,需瀏覽 12分鐘

           ·

          2021-03-06 18:46


          文章導(dǎo)讀

          本文是一篇工程開(kāi)發(fā)方面的文章,算法的落地還是需要工程代碼的依托,比如在激光雷達(dá)感知模塊中如果既包含了傳統(tǒng)點(diǎn)云處理做障礙物檢測(cè),又使用深度學(xué)習(xí)做點(diǎn)云目標(biāo)識(shí)別,往往需要采用多線(xiàn)程并行處理兩個(gè)分支,再將輸出結(jié)果融合后送入跟蹤模塊,這里簡(jiǎn)單介紹下C++的多線(xiàn)程知識(shí)。


          目錄

          • 多線(xiàn)程原理

          • 多線(xiàn)程同步和互斥

          • 多線(xiàn)程示例代碼


          多線(xiàn)程原理

          線(xiàn)程是進(jìn)程中的一個(gè)實(shí)體,是被系統(tǒng)獨(dú)立分配和調(diào)度的基本單位。也就是說(shuō)線(xiàn)程是CPU可執(zhí)行調(diào)度的最小單位。引入線(xiàn)程之后將進(jìn)程的兩個(gè)基本屬性分開(kāi)了,線(xiàn)程作為調(diào)度和分配的基本單位,進(jìn)程作為獨(dú)立分配資源的單位。


          線(xiàn)程基本上不擁有資源,只擁有一點(diǎn)運(yùn)行中必不可少的資源,在同一個(gè)進(jìn)程中的所有線(xiàn)程都共享地址空間,線(xiàn)程間的大部分?jǐn)?shù)據(jù)可以共享并且相比多進(jìn)程來(lái)說(shuō),多線(xiàn)程間的通信開(kāi)銷(xiāo)小、啟動(dòng)速度快、占用資源少。


          為什么要進(jìn)行多線(xiàn)程并發(fā)呢?

          主要是用于任務(wù)拆分和提高性能:將程序劃分成不同的任務(wù),每個(gè)線(xiàn)程執(zhí)行一個(gè)或多個(gè)任務(wù),可以將整個(gè)程序的邏輯變的更加清晰;任務(wù)之間各自并發(fā)降低運(yùn)算時(shí)間。


          談到多線(xiàn)程我們經(jīng)常聽(tīng)到并發(fā)、并行的概念,并發(fā)與并行有什么區(qū)別呢?

          串行很容易理解就是說(shuō)所有任務(wù)都按先后順序執(zhí)行。而并行和并發(fā)的區(qū)別在于多個(gè)任務(wù)是否同時(shí)執(zhí)行。


          并發(fā)是指在同一時(shí)間段內(nèi),能夠處理多個(gè)事件,在并發(fā)程序中可以同時(shí)擁有兩個(gè)或者多個(gè)線(xiàn)程。這意味著,如果程序在單核處理器上運(yùn)行,那么這兩個(gè)線(xiàn)程將交替地?fù)Q入或者換出內(nèi)存。這些線(xiàn)程是同時(shí)“存在”的——每個(gè)線(xiàn)程都處于執(zhí)行過(guò)程中的某個(gè)狀態(tài)。 


          并行是指在同一時(shí)刻上,能夠處理多個(gè)事件。如果程序能夠并行執(zhí)行,那么就一定是運(yùn)行在多核處理器上。此時(shí)程序中的每個(gè)線(xiàn)程都將分配到一個(gè)獨(dú)立的處理器核上,因此可以同時(shí)運(yùn)行。


          多線(xiàn)程同步和互斥

          多個(gè)線(xiàn)程同時(shí)執(zhí)行任務(wù)肯定存在線(xiàn)程間的同步和互斥:

          1. 線(xiàn)程同步:指線(xiàn)程之間所具有的一種制約關(guān)系,一個(gè)線(xiàn)程的執(zhí)行依賴(lài)另外一個(gè)線(xiàn)程的消息,當(dāng)它沒(méi)有得到另一個(gè)線(xiàn)程的消息時(shí)應(yīng)等待,直到消息到達(dá)時(shí)才被喚醒。


          2. 線(xiàn)程互斥:指對(duì)于共享的進(jìn)程系統(tǒng)資源,每個(gè)線(xiàn)程訪問(wèn)時(shí)的排他性。當(dāng)有若干個(gè)線(xiàn)程都要使用某一個(gè)共享資源時(shí),任何時(shí)刻最多只允許一個(gè)線(xiàn)程去使用,其他線(xiàn)程必須等待,知道占用占用資源者釋放該資源。線(xiàn)程互斥可以看成是一種特殊的線(xiàn)程同步。


          線(xiàn)程間的同步方法大體可以分為兩類(lèi):

          1. 用戶(hù)模式(使用時(shí)不需要切換內(nèi)核態(tài),只在用戶(hù)態(tài)完成操作):

          • 臨界區(qū):適合一個(gè)進(jìn)程內(nèi)的多線(xiàn)程訪問(wèn)公共區(qū)域或代碼段時(shí)使用;


          2. 內(nèi)核模式(利用系統(tǒng)內(nèi)核對(duì)象的單一性來(lái)進(jìn)行同步,使用時(shí)需要切換內(nèi)核態(tài)與用戶(hù)態(tài)):

          • 事件:通過(guò)線(xiàn)程間觸發(fā)事件實(shí)現(xiàn)同步互斥;

          • 互斥量:適合不同進(jìn)程內(nèi)多線(xiàn)程訪問(wèn)公共區(qū)域或代碼段時(shí)使用,與臨界區(qū)相似;

          • 信號(hào)量:與臨界區(qū)和互斥量不同,可以實(shí)現(xiàn)多個(gè)線(xiàn)程同時(shí)訪問(wèn)公共區(qū)域數(shù)據(jù),原理與操作系統(tǒng)中PV操作類(lèi)似,先設(shè)置一個(gè)訪問(wèn)公共區(qū)域的線(xiàn)程最大連接數(shù),每有一個(gè)線(xiàn)程訪問(wèn)共享區(qū)資源數(shù)就減一,直到資源數(shù)小于等于零;



          多線(xiàn)程示例代碼

          C++11中提供了thread庫(kù)管理線(xiàn)程、保護(hù)共享數(shù)據(jù)、線(xiàn)程間同步等功能。頭文件是#include<thread>。一個(gè)進(jìn)程至少要有一個(gè)線(xiàn)程,在C++中可以認(rèn)為main函數(shù)就是我們的主線(xiàn)程。而在創(chuàng)建thread對(duì)象的時(shí)候,就是在這個(gè)線(xiàn)程之外創(chuàng)建了一個(gè)獨(dú)立的子線(xiàn)程。只要?jiǎng)?chuàng)建了這個(gè)子線(xiàn)程并且開(kāi)始運(yùn)行了,主線(xiàn)程就完全和它沒(méi)有關(guān)系了,不知道CPU會(huì)什么時(shí)候調(diào)度它運(yùn)行,什么時(shí)候結(jié)束運(yùn)行。


          如何創(chuàng)建、啟動(dòng)、結(jié)束多線(xiàn)程?


          C++有三種創(chuàng)建線(xiàn)程的方式:

          1. 通過(guò)一個(gè)初始函數(shù)創(chuàng)建線(xiàn)程

          #include <iostream>#include <thread>using namespace std;void RecogniseThread(){    std::cout << "DL Recognise point cloud." << std::endl;}int main(){    thread task(RecogniseThread);    task.join();    //task.detach();    std::cout << "執(zhí)行主線(xiàn)程" << std::endl;        return 0;}
          • 上例構(gòu)建一個(gè)thread對(duì)象task,構(gòu)造的時(shí)候傳遞一個(gè)函數(shù)名進(jìn)去,它就是這個(gè)線(xiàn)程的入口函數(shù),函數(shù)執(zhí)行完表示該線(xiàn)程也執(zhí)行結(jié)束;

          • 線(xiàn)程創(chuàng)建成功后就立即啟動(dòng),并沒(méi)有一個(gè)開(kāi)始的API觸發(fā)啟動(dòng)線(xiàn)程;

          • 線(xiàn)程運(yùn)行后需要顯示的決定程序是否用join阻塞等待它完成,并回收該線(xiàn)程中使用的資源或者detach分離出去讓他自行運(yùn)作;


          2. 通過(guò)類(lèi)對(duì)象創(chuàng)建線(xiàn)程

          通過(guò)類(lèi)來(lái)構(gòu)造線(xiàn)程其實(shí)是仿函數(shù)的功能。仿函數(shù)是使用類(lèi)來(lái)模擬函數(shù)調(diào)用行為,只要重載一個(gè)類(lèi)的operator()方法,即可像調(diào)用一個(gè)函數(shù)一樣調(diào)用類(lèi)。提供給thread實(shí)例的函數(shù)對(duì)象會(huì)復(fù)制到新線(xiàn)程的存儲(chǔ)空間中,函數(shù)對(duì)象的執(zhí)行和調(diào)用都在線(xiàn)程的內(nèi)存空間中執(zhí)行。

          #include <iostream>#include <thread>using namespace std;class Recognise{    void operator()(){        std::this_thread::sleep_for(std::chrono::milliseconds(1000));        std::cout << "子線(xiàn)程延遲1s." << std::endl;    }}
          int main(){ thread task(Recognise); task.detach(); std::cout << "主線(xiàn)程結(jié)束" << std::endl; return 0;}
          • join用于阻塞線(xiàn)程,所以主線(xiàn)程要等待子線(xiàn)程執(zhí)行完畢才會(huì)繼續(xù)向下執(zhí)行;調(diào)用join還會(huì)清理線(xiàn)程相關(guān)的存儲(chǔ)部分,這表示join只能調(diào)用一次。使用joinable()來(lái)判斷join()可否調(diào)用。

          • detach用于主線(xiàn)程和當(dāng)前線(xiàn)程分離,主線(xiàn)程可以先執(zhí)行結(jié)束,如果主線(xiàn)程執(zhí)行完了,子線(xiàn)程會(huì)在C++后臺(tái)運(yùn)行,一旦使用detach,與這個(gè)子線(xiàn)程關(guān)聯(lián)的對(duì)象會(huì)失去對(duì)這個(gè)主線(xiàn)程的關(guān)聯(lián),此時(shí)這個(gè)子線(xiàn)程會(huì)駐留在C++后臺(tái)運(yùn)行,當(dāng)主線(xiàn)程執(zhí)行完畢結(jié)束,子線(xiàn)程會(huì)移交給C++運(yùn)行時(shí)庫(kù)管理,這個(gè)運(yùn)行時(shí)庫(kù)會(huì)清理與這個(gè)線(xiàn)程相關(guān)的資源(守護(hù)線(xiàn)程),detach會(huì)使子線(xiàn)程失去進(jìn)程的控制;

          • 開(kāi)啟線(xiàn)程后必須顯示的決定主線(xiàn)程是等待還是分離子線(xiàn)程,否則會(huì)引起程序崩潰。

          • 上例中讓子線(xiàn)程延遲1s目的是讓主線(xiàn)程先結(jié)束,task對(duì)象被銷(xiāo)毀,但是由于detach分離出線(xiàn)程,所以子線(xiàn)程仍可以脫離主線(xiàn)程運(yùn)行打印;


          3. 通過(guò)lambda表達(dá)式創(chuàng)建線(xiàn)程

          #include <iostream>#include <thread>using namespace std;int main(){    auto lambda_thread = [] {        std::this_thread::sleep_for(std::chrono::milliseconds(1000));        std::cout << "子線(xiàn)程延遲1s." << std::endl;    };    thread task(lambda_thread);    task.detach();    std::cout << "主線(xiàn)程結(jié)束" << std::endl;        return 0;}



          線(xiàn)程如何調(diào)用類(lèi)成員函數(shù)并傳入?yún)?shù)?

          #include <iostream>#include <thread>class LidarProcess {public:    void RecogniseThread(const PointCloudPtr &input_cloud_ptr){        std::cout << "Process DL Inference." << std::endl;    }};int main(){    LidarProcess lidar_process;    PointCloudPtr inlier_cloud_ptr(new PointCloud);    std::thread task(std::bind(&LidarProcess::RecogniseThread, this, inlier_cloud_ptr));    task.join();     return 0;}


          創(chuàng)建一個(gè)LidarProcess類(lèi),在主函數(shù)中將類(lèi)LidarProcess中的成員函數(shù)綁定到線(xiàn)程對(duì)象task上。截取的工程代碼,有些結(jié)構(gòu)體定義未放出來(lái),比如PointCloudPtr 是智能指針,所以在這就沒(méi)有delete了。


          多線(xiàn)程如何進(jìn)行同步?

          互斥量是為了解決數(shù)據(jù)共享過(guò)程中可能存在的訪問(wèn)沖突的問(wèn)題。在C++11中提供4互斥量:

          std::mutex;                  //非遞歸的互斥量std::timed_mutex;            //帶超時(shí)的非遞歸互斥量std::recursive_mutex;        //遞歸互斥量std::recursive_timed_mutex;  //帶超時(shí)的遞歸互斥量


          其中最常用的是mutex,通過(guò)實(shí)例化mutex創(chuàng)建互斥量,在進(jìn)入臨界區(qū)之前調(diào)用成員函數(shù)lock進(jìn)行上鎖,退出臨界區(qū)時(shí)對(duì)互斥量unlock進(jìn)行解鎖,當(dāng)一個(gè)線(xiàn)程使用特定互斥量鎖住共享數(shù)據(jù)時(shí),其他的線(xiàn)程想要訪問(wèn)鎖住的數(shù)據(jù),都必須等到之前那個(gè)線(xiàn)程對(duì)數(shù)據(jù)進(jìn)行解鎖后,才能進(jìn)行訪問(wèn)。(這種方式需要在每個(gè)函數(shù)的出口或者異常都去調(diào)用unlock)

          mutex mx;            //實(shí)例化mutexvector<BBox> bboxes; //用于DL推理后存儲(chǔ)Bounding box的容器
          vector<BBox> RecogniseObject(const PointCloudPtr input_cloud_ptr){...}
          void RecogniseThread(const PointCloudPtr input_cloud_ptr){ mu.lock(); //如果這句話(huà)拋出異常,mu永遠(yuǎn)會(huì)被鎖住 bboxes = RecogniseObject(input_cloud_ptr); mu.unlock();}


          因?yàn)橥ㄟ^(guò)lock與unclock可以解決線(xiàn)程之間的資源競(jìng)爭(zhēng)問(wèn)題,如果在RecogniseObject中執(zhí)行識(shí)別時(shí)程序因?yàn)槟承┰蛲顺隽?,此時(shí)就無(wú)法unlock,這樣其他線(xiàn)程也就無(wú)法獲取mutex資源從而造成死鎖現(xiàn)象,其實(shí)在加鎖之前可以通過(guò)trylock()嘗試一下能不能加鎖。但更好的方式是采用lock_guard或者unique_lock來(lái)控制std::mutex。所以C++提供了一種RAII(Resource Acquisition Is Initialization 資源獲取即初始化)方式的模板類(lèi)lock_guard,會(huì)在構(gòu)造的時(shí)候提供加鎖的互斥量,并在析構(gòu)的時(shí)候進(jìn)行解鎖,從而保證了一個(gè)已鎖的互斥量總是會(huì)被正確的解鎖。(unique_lock類(lèi)似)

          mutex mx;            //實(shí)例化mutexvector<BBox> bboxes; //用于DL推理后存儲(chǔ)Bounding box的容器
          vector<BBox> RecogniseObject(const PointCloudPtr input_cloud_ptr){...}
          void RecogniseThread(const PointCloudPtr input_cloud_ptr){ lock_guard<mutex> guard(mu); //當(dāng)此句異常,mu對(duì)象自動(dòng)被解鎖 bboxes = RecogniseObject(input_cloud_ptr);}


          大多數(shù)情況下,互斥量會(huì)和被保護(hù)的數(shù)據(jù)放在同一個(gè)類(lèi)中,而不是定義成全局變量。二者均定義成private成員?;コ饬勘Wo(hù)的數(shù)據(jù)需要對(duì)接口的設(shè)計(jì)相當(dāng)謹(jǐn)慎,要確保互斥量能鎖住任何對(duì)保護(hù)數(shù)據(jù)的訪問(wèn),尤其是需要傳遞指針和引用時(shí)。


          如何轉(zhuǎn)移線(xiàn)程的所有權(quán)?

          thread是可移動(dòng)的(movable)的,但不可復(fù)制(copyable)。移動(dòng)拷貝或者移動(dòng)賦值都使得原有對(duì)象對(duì)所屬資源的控制權(quán)發(fā)生轉(zhuǎn)移,從對(duì)象A轉(zhuǎn)移到對(duì)象B,對(duì)資源的控制只在對(duì)象B中保留。在C++ 11中標(biāo)準(zhǔn)庫(kù)中,提供了std::move函數(shù)用于資源移動(dòng)操作來(lái)改變線(xiàn)程的所有權(quán),靈活的決定線(xiàn)程在什么時(shí)候join或者detach。

          thread task_1(RecogniseThread);thread task_2(move(task_1));


          將線(xiàn)程從task_1轉(zhuǎn)移給task_2,這時(shí)候task_1就不再擁有線(xiàn)程的所有權(quán),調(diào)用task_1.join()或task_1.detach()會(huì)出現(xiàn)異常,要使用task2來(lái)管理線(xiàn)程,此時(shí)task1的ID變?yōu)?,joinable變?yōu)闉閒alse。這也就意味著thread可以作為函數(shù)的返回類(lèi)型,或者作為參數(shù)傳遞給函數(shù),能夠更為方便的管理線(xiàn)程。



            ?------------------------------------------------


            看到這里了,說(shuō)明您也喜歡這篇文章,您可以點(diǎn)擊「分享」與朋友們交流,點(diǎn)擊「在看」使我們的新文章及時(shí)出現(xiàn)在您的訂閱列表中,或順手「點(diǎn)贊」給我們一個(gè)支持,讓我們做的更好哦。


            歡迎微信搜索并關(guān)注「目標(biāo)檢測(cè)與深度學(xué)習(xí)」,不被垃圾信息干擾,只分享有價(jià)值知識(shí)!


          瀏覽 142
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  日本女人受爱高潮视频网站 | 欧美精品手机在线 | 欧美成人性生交 | 好吊视频一区二区三区 | 3级片在线 |