1. 程式人生 > 其它 >C++中的類加多執行緒程式碼修煉

C++中的類加多執行緒程式碼修煉

技術標籤:多執行緒c++

背景:現在在做一個目標跟蹤的專案,需要實時的從工業相機中獲取影象,然後再跟蹤影象上的目標物,由於起初為了測試跟蹤演算法,就把“從相機獲取影象”和“跟蹤處理”都放在了主執行緒中,在實際測試時,直接從相機獲取影象時,跟蹤處理部分幀率出現週期性卡頓的問題,而直接讀取本地的視訊資料時跟蹤部分幀率很穩。因為“獲取影象”和“跟蹤處理”在一個執行緒中,所以兩者是一條線上的螞蚱,要快都快,要慢都滿,所以我沒必要在幀率測試上花時間了,另外考慮到,相機應該單獨出來,一個是將一些相機的操作封裝到一個類裡面,另外需要將他放在一個執行緒中去,這樣這個執行緒只顧獲取影象就可以不受其他操作的影響。又考慮到以後還會有資料處理這一塊,所以把跟蹤這塊也封裝到一個類中,也放在一個單獨的執行緒中去。

說自己是C++程式猿真是慚愧,使用C++也快5年了,期間也專門拿兩個月專門來學習C++語法,但是一直以來練習的太少,導致現在的水平依然很低,今天問我老同事相關問題,他直接給我發了一個“頭上頂著菜葉子的狗”,名副其實呀,我感覺我就是個菜狗,哈哈,我要儘早擺脫這個稱號。

雖然我的C++功底不深厚,但是還是有一些基礎和經驗的,我知道第一步需要做的就是把架子搭建起來,然後通過了,然後再往裡面填東西。

獲取影象的類 CaptureThread.h

#ifndef CAPTURETHREAD_H
#define CAPTURETHREAD_H
namespace FDSST {
class CaptureThread {
public:
	CaptureThread();
	~CaptureThread();
	void Run();
	void Stream();
	void Pause();
	void Stop();
	bool quit;
private:
	bool pause_status;
};
}
#endif // CAPTURETHREAD_H

獲取影象類實現:CaptureThread.cpp

#include <iostream>
#include "../include/capturethread.h"
#include <windows.h>

namespace FDSST
{
CaptureThread::CaptureThread()
{

	pause_status = false;
	quit = false;
}
CaptureThread::~CaptureThread()
{
}
void CaptureThread::Run()
{
    std::cout << "capture_thread!!!" << std::endl;
	return;
}
void CaptureThread::Stream()
{
	pause_status = false;
}

void CaptureThread::Pause()
{
	pause_status = true;
}

void CaptureThread::Stop()
{
	pause_status = true;
	quit = true;
}
}

跟蹤類:TrackingThread.h

#ifndef TRACKINGTHREAD_H
#define TRACKINGTHREAD_H

namespace FDSST
{
class Tracking {
public:
	Tracking();
	~Tracking();
	void Run();
};
}
#endif

跟蹤類實現:TrackingThread.cpp

#include <iostream>
#include "../include/trackingthread.h"
#include <windows.h>
namespace FDSST
{
	Tracking::Tracking(){}
	Tracking::~Tracking(){}
	void Tracking::Run()
	{
		std::cout << "tracking_thread!!!" << std::endl;
        return;
	}
}

我學著orb-slam程式碼結構的樣子,整出來一個System類:System.h

#ifndef SYSTEM_H
#define SYSTEM_H
#include <thread>
#include "../include/Trackingthread.h"
#include "../include/capturethread.h"
namespace FDSST
{
class Tracking;
class CaptureThread;

class System {
public:
	System();
	~System();
private:
	Tracking* mpTracker;
	CaptureThread* mpCapturer;

	std::thread* mptTracking;
	std::thread* mptCapturing;
};
}
#endif

Syetem.cpp

在System類的建構函式中,建立兩個執行緒,將CaptureThread類和Tracking類中的成員函式Run作為執行緒的入口

#include "../include/System.h"
#include <thread>

namespace FDSST {
	System::System()
	{
		mpCapturer = new CaptureThread();
		mptCapturing = new std::thread(&FDSST::CaptureThread::Run, mpCapturer);
        std::cout << "Capture thread has been created" << std::endl;

		mpTracker = new Tracking();
		mptTracking = new std::thread(&FDSST::Tracking::Run, mpTracker);
        std::cout << "Tracking thread has been created" << std::endl;
	}
	System::~System()
	{
	
	}
}

我們想象的多執行緒的樣子都是,程式碼執行起來之後,兩個執行緒中程式碼段一直在執行,直到到達程式碼控制它結束的時候。看我在主函式中是怎麼呼叫System的建構函式,從而建立兩個執行緒的。

我最初的寫法是這樣的:

#include <iostream>
#include "../include/System.h"
#include <windows.h>

int main(int argc, char** argv)
{
	FDSST::System TRACK();
	return 0;
}

此時程式碼可以編譯通過,但是我發現程式並沒有進入到System類中的建構函式中去,所以執行緒被建立的列印未輸出。我很納悶,為啥那塊程式碼沒有被執行呢?

我就去請教我的老同事,一個很低調很牛的boy,我把主函式和System.cpp的內容截圖發給他,他一看就知道了,他讓我把FDSST::System TRACK();中的括號去掉,原因是,我在System.cpp中的預設建構函式中執行建立執行緒的操作,而如果我的TRACK後面帶有括號,就不會呼叫預設建構函式,我這種寫法在某些編譯器上應該會出錯的。

也就是說,
如果我使用的是預設的建構函式那麼就得這樣定義 :FDSST::System TRACK;
如果我定義了帶引數的建構函式,那就可以定義為:FDSST::System TRACK(paras);

上面的問題解決之後,CaptureThread和TrackingThread類中Run函式中的打操作只被執行了一次,我就又納悶了,咋跟我想象的不一樣,應該一直執行下去,我繼續問我那個老同事事,他說需要加上while(1),我知道了是在Run函式裡面加while(1).上面的Run函式改成下面這種寫法。

void CaptureThread::Run()
{
	while (1)
	{
		std::cout << "capture_thread!!!" << std::endl;
	}
	return;
}
void Tracking::Run()
{
	while (1)
	{
		std::cout << "tracking_thread!!!" << std::endl;
	}
    return;
}

然後我發現Run函式中的列印操作還是隻執行了一次,我又納悶了,又問我那老同事,他說,"你的主函式結束了吧”,讓我在main函式中新增Sleep()函式。我把主函式改成下面這個樣子

#include <iostream>
#include "../include/System.h"
#include <windows.h>

int main(int argc, char** argv)
{
	FDSST::System TRACK();
    Sleep(10000);
	return 0;
}

修改之後,程式碼可以一直執行了,但是我發現,兩個執行緒的輸出呈現交替執行的現象,一個執行緒執行很多次,然後另外一個執行緒再執行很多次,交替執行,像下面這樣。

我又問我老同事,他讓我在每一個Run函式中也加上Sleep(),我就把Run函式中加上Sleep()

void CaptureThread::Run()
{
	while (1)
	{
		std::cout << "capture_thread!!!" << std::endl;
        Sleep(2);
	}
	return;
}
void Tracking::Run()
{
	while (1)
	{
		std::cout << "tracking_thread!!!" << std::endl;
        Sleep(2);
	}
    return;
}

這下再執行:就正常了,接下來就可以往裡面實現功能了。雖然這個框架很簡單,但是在搭建出來的過程中讓我學到很多東西,所以將難得問題拆分成簡單的問題,一步一步的來,就可以解決。