關於實時檢測中的多線程並發處理

碼道人 2021-08-15 20:41:59 阅读数:863

本文一共[544]字,预计阅读时长:1分钟~

在實時檢測中為了能够更高效地利用CPU性能,常需要使用到多線程並發處理,但由於實時檢測中每一幀都在刷新內存,而多線程編程最大的難點就在於容易造成內存沖突,所以本文章將介紹以下關於如何實現在實時檢測中多線程編程的方法。

簡介

線程是操作系統能够進行CPU調度的最小單比特,它被包含在進程之中,一個進程可包含單個或者多個線程。可以用多個線程去完成一個任務,也可以用多個進程去完成一個任務,它們的本質都相當於多個人去合夥完成一件事。

C++多線程並發: (簡單情况下)實現C++多線程並發程序的思路如下:將任務的不同功能交由多個函數分別實現,創建多個線程,每個線程執行一個函數,一個任務就這樣同時分由不同線程執行了。

C++11 新標准中引入了四個頭文件來支持多線程編程,他們分別是<atomic> ,<thread>,<mutex>,<condition_variable>和<future>。這裏簡單介紹一下前三個:

<atomic>:該頭文主要聲明了兩個類, std::atomic 和 std::atomic_flag,另外還聲明了一套 C 風格的原子類型和與 C 兼容的原子操作的函數。

<thread>:該頭文件主要聲明了 std::thread 類,另外 std::this_thread 命名空間也在該頭文件中。

<mutex>:該頭文件主要聲明了與互斥量(mutex)相關的類,包括 std::mutex 系列類,std::lock_guard, std::unique_lock, 以及其他的類型和函數。

實現

關於如何實現多線程並發處理,我們需要解决三個問題:

1.賦予子線程任務

這個問題不大,只需要注意std::thread的用法即可,賦予線程任務常常使用的時std::thread的構造函數來實現的,函數的第一個參數為需要執行的任務,後面為對應的參數。

賦予任務時的第一個參數應該用的時函數的地址,可以選擇傳入靜態函數或者傳入實例化類成員函數的地址都行。

2.內存沖突

多線程最經典的問題就是不同線程同時訪問或修改同一塊內存,這樣就很容易造成內存沖突從而報錯。這裏我們就需要用到互斥鎖,並且為了盡可能减少相同內存的占用,我們可以將所需要用到的公共內存部分在加互斥鎖後進行拷貝,拷貝完再解鎖。並發的線程訪問的內存為拷貝的那一部分,這樣就相當於將同一份內存拷貝後分隔開,這也是解决問題的一個較為簡單方便的方法。

在這裏簡單介紹以下互斥鎖:

互斥鎖的特點就是在加鎖後訪問的內存,其他線程無法進行幹涉,只有當解鎖後才能訪問那一塊內存。使用前需要引入<mutex>頭文件,使用起來很簡單,只需要實例化後進行加鎖和解鎖:

static std::mutex Matlock;
// 加鎖
Matlock.lock();
// 解鎖
Matlock.unlock();

死鎖問題

死鎖就是多個線程爭奪共享資源導致每個線程都不能取得自己所需的全部資源,從而程序無法向下執行。

產生死鎖的四個必要條件:

  1. 互斥(資源同一時刻只能被一個進程使用)
  2. 請求並保持(進程在請資源時,不釋放自己已經占有的資源)
  3. 不剝奪(進程已經獲得的資源,在進程使用完前,不能强制剝奪)
  4. 循環等待(進程間形成環狀的資源循環等待關系)

死鎖避免: 對分配資源做安全性檢查,確保不會產生循環等待。

3.實時檢測中的線程管理

在圖像實時檢測時,假如我們每一幀都要開啟新的線程並發運行,但由於主線程和子線程運行並不同步,就有可能出現主線程運行到下一次要開啟新線程的時候,子線程還沒有運行結束,此時重新賦予子線程新的任務就會導致程序運行失敗。

解决方法:

在給子線程賦予新任務前將子線程與任務進行分離,保證在給子線程賦予新任務時子線程已經處於空閑狀態。

實現起來很簡單,只需要判斷可以分離的時候加入detach()即可,即:

if (thread.joinable())
thread.detach();

注意

多線程編程的實現其實不難,難在其中的邏輯排列和內存使用。稍微不注意在計算機高速運行的情况下就有可能出現千千萬萬種錯誤。

需要注意的是並不是只要開了多線程並發處理,我們的程序運行速度就能够有所提昇。當CPU占用率很高時,即使開了多線程,CPU也無法同時完成多個任務,此時多線程反而會降低程序的運行速度。

參考內容

C++ std::thread

版权声明:本文为[碼道人]所创,转载请带上原文链接,感谢。 https://gsmany.com/2021/08/20210815204156331e.html