쓰레드
쓰레드 구문
#include <thread>
detach()는 결과값을 기다리지 않고 다음 코드를 실행시키는 함수이다.
join은 다른 쓰레드들이 끝날 때까지 기다려서 합친 후 실행시킨다.
쓰레드 개념을 공부하기 위해 따로 정리해 보았다.
전체 코드
함수 인클루드
#include "Process.h" // 프로세스 정보를 가져옴
#include <chrono> // 크로노 함수
#include <thread>
using namespace std; // std::를 축약해 준다.
using namespace chrono; // std::chrono::를 축약해 준다.
void CgPrjDlg::OnBnClickedBtnThread()
{
// TODO: 여기에 컨트롤 알림 처리기 코드를 추가합니다.
auto start = system_clock::now();
int nImgSize = 4096 * 4;
CRect rect(0, 0, nImgSize, nImgSize);
CRect rt[4];
int nRet[4];
for (int k = 0; k < 4; k++) {
rt[k] = rect;
rt[k].OffsetRect(nImgSize*(k % 2), nImgSize*int(k / 2));
}
thread _thread0(threadProcess, this, rt[0], &nRet[0]);
thread _thread1(threadProcess, this, rt[1], &nRet[1]);
thread _thread2(threadProcess, this, rt[2], &nRet[2]);
thread _thread3(threadProcess, this, rt[3], &nRet[3]);
_thread0.join();
_thread1.join();
_thread2.join();
_thread3.join();
int nSum;
for (int k = 0; k < 4; k++)
nSum += nRet[k];
auto end = system_clock::now();
auto millisec = duration_cast<milliseconds>(end - start);
cout << nSum << "\t" << millisec.count()*0.001 << "sec" << endl;
}
코드 소요시간 측정 코드 만들기
실행시간을 측정하기 위해 기존 이미지 크기를 크게 변경시킨다.(소요시간이 너무 짧지 않게 하기 위해)
auto start = system_clock::now(); // 시작시간
// 여기에 측정할 코드가 들어갑니다.
auto end = system_colok::now(); // 끝 시간
auto millisec = duration_cast<milliseconds>(end - start);
cout << milliseconds.count() << endl;
가운데에 측정할 코드가 들어간다.
이미지 사이즈 조정 및 영역분할
// 전체 영역을 4군데로 나눌것이기 때문에 이미지 변경
int nImgSize = 4096 * 4;
CRect rect(0, 0, nImgSize, nImgSize);
CRect rt[4]; // 4개의 영역으로 분할
for (int k = 0; k < 4;k++) {
rt[k] = rect;
rt[k].OffsetRect(nImgSize*(k%2), nImgSize * int(k/2)); // 일정영역만큼 x, y 만큼 움직여라
}
nImgSize = 4096 * 4;
전체 영역의 크기를 설정한다. 각 변의 길이는 4096에 4를 곱한 값인 16384이다.
CRect rect(0, 0, nImgSize, nImgSize);: CRect
직사각형을 나타내는 클래스이다. 여기서는 시작 지점(0, 0)에서 (nImgSize, nImgSize)까지의 직사각형을 만든다.
전체 영역을 나타낸다.
CRect rt[4]
4개의 CRect 객체를 저장할 배열을 선언한다. 이 배열은 rect를 4개의 동일한 부분으로 나누어 저장하기 위한 것이다.
for (int k = 0; k < 4;k++)
for문을 사용해 0번부터 3번까지 총 4번 반복하는 루프이다.
- rt[k] = rect;:
- 초기에 각 rt[k]는 전체 영역을 나타내는 rect로 설정된다.
- rt[k].OffsetRect(nImgSize*(k%2), nImgSize * int(k/2));:
- OffsetRect는 주어진 CRect 객체를 x와 y 방향으로 일정 거리만큼 이동시키는 메서드이다.
- nImgSize*(k%2):
- x방향의 이동 거리이다.
- k가 0 또는 2일 때(짝수일때)는 0만큼, k가 1 또는 3일 때(홀수일때)는 nImgSize만큼 이동한다.
- nImgSize * int(k/2):
- y방향의 이동 거리이다.
- k가 0 또는 1일 때는 0만큼, k가 2 또는 3일 때는 nImgSize만큼 이동한다.
아래와 같이 전체 영역이 4개의 동일한 영역으로 나뉘게 된다.
+-----+-----+
| rt[0] | rt[1] |
+-----+-----+
| rt[2] | rt[3] |
+-----+-----+
각 k에 따른 offsetRect의 값은 다음과 같다.
k=0일 때, OffsetRect(0, 0)
k=1일 때, OffsetRect(16384, 0)
k=2일 때, OffsetRect(0, 16384)
k=3일 때, OffsetRect(16384, 16384)
사이즈는 모두 동일하지만 4분할 했을 때 좌표만 다름을 확인할 수 있다.
쓰레드 사용
함수 위에 thread를 include 시킨다.
(원래는 헤더 파일에 선언하는게 올바름)
헤더에 선언해주고
쓰레드 버튼을 클릭했을 때 타는 코드를 마저 작성해 준다.
#include <thread>
// 글로벌로 선언해 준다. 적용할 함수 위에 작성한다.
void threadProcess(Cwnd* pParent, CRect rect);
{
CgPrjDlg *pWnd = (CgPrjDlg*)pParent;
pWnd->processImg
}
void CgPrjDlg::OnBnClickedBtnThread()
{
auto start = system_clock::now(); // 시작시간
// 전체 영역을 4군데로 나눌것이기 때문에 이미지 변경
int nImgSize = 4096 * 4;
CRect rect(0, 0, nImgSize, nImgSize);
CRect rt[4]; // 4개의 영역으로 분할
for (int k = 0; k < 4;k++) {
rt[k] = rect;
rt[k].OffsetRect(nImgSize*(k%2), nImgSize * int(k/2)); // 일정영역만큼 x, y 만큼 움직여라
}
// 쓰레드 프로세스 함수 쓸 것이고, this에서 사용할 것
thread _thread0(threadProcess, this, rt[0]);// 쓰레드 사용할 때마다 검사영역 주기
auto end = system_colok::now(); // 끝 시간
auto millisec = duration_cast<milliseconds>(end - start);
cout << milliseconds.count() << endl;
}
여기서 _thread0은 바로 위에 있는 threadProcess를 call 하는데, 이 threadProcess에 줄 때 parent를 줬기 때문에
parent(pWnd)에서 processImg를 하게 되고(그 코드에는 processing 함수를 call)
processing 함수는 이 코드들 바로 밑에 작성한다.
int CgPrjDlg::processImg(CRect rect)
{
auto start = system_clock::now();
CProcess process;
int nTh = 0;
int nRet = process.getStarInfo(&m_pDlgImage->m_image, nTh, rect);
auto end = system_clock::now();
auto millisec = duration_cast<milliseconds>(end - start);
cout << nRet << "\t" << millisec.count()*0.001 << "sec" << endl;
return nRet;
}
프로세스 이미지 함수를 생성. 여기서 사용하는 getStarInfo 함수를 새로 생성해 보자.
process.cpp 파일에서 수정한 코드.
int CProcess::getStarInfo(CImage* pImage, int nTh, CRect rect)
{
unsigned char* fm = (unsigned char*)pImage->GetBits();
int nWidth = pImage->GetWidth();
int nHeight = pImage->GetHeight();
int nPitch = pImage->GetPitch();
int nSum = 0;
for (int j = rect.top; j < rect.bottom; j++) {
for (int i = rect.left; i < rect.right; i++) {
if (fm[j*nPitch + i] > nTh) { // nTh보다 크면 nSum++을 해 준다.
nSum++;
}
}
}
return nSum;
}
rect변수를 추가하여 이 rect의 크기 안에서만 nTh보다 큰 수를 센다.
void CgPrjDlg::OnBnClickedBtnThread()
{
// TODO: 여기에 컨트롤 알림 처리기 코드를 추가합니다.
auto start = system_clock::now();
int nImgSize = 4096 * 4;
CRect rect(0, 0, nImgSize, nImgSize);
CRect rt[4];
int nRet[4]; // 변수를 얻기 위해서
for (int k = 0; k < 4; k++) {
rt[k] = rect;
rt[k].OffsetRect(nImgSize*(k % 2), nImgSize*int(k / 2));
}
thread _thread0(threadProcess, this, rt[0], &nRet[0]); // 쓰레드, 실행, 주어지는 값, 결과값
thread _thread1(threadProcess, this, rt[1], &nRet[1]);
thread _thread2(threadProcess, this, rt[2], &nRet[2]);
thread _thread3(threadProcess, this, rt[3], &nRet[3]);
_thread0.join();
_thread1.join();
_thread2.join();
_thread3.join();
int nSum=0;
for (int k = 0; k < 4; k++)
nSum += nRet[k];
auto end = system_clock::now();
auto millisec = duration_cast<milliseconds>(end - start);
cout << nSum << "\t" << millisec.count()*0.001 << "sec" << endl;
}
그리고 쓰레드를 4개로 나눈다.
픽셀값을 얻기 위해서는) 결과값 nRect를 써야 한다.
- 쓰레드는 리턴값이 없기 때문에 구조체든 변수든, 주어질 때 함수를 통해 얻어내야 한다.
cout << nRet << "\t" << millisec.count()*0.001 << "sec" << endl;
- 쓰레드 시간 결과값을 얻기 위해 측정 시간값을 동일게 작성해주고 출력한다.
일반 실행은 2초 걸렸는데, 쓰레드를 사용하니 그 4분의 1값이 나왔다.
detach 와 join 의 차이점
- detach: 이미지 저장할 때 처럼 기다릴 필요가 없을 때, 끝날때까지 기다리지 않고 종료하는 메서드
- join: 값의 합처럼 다른 쓰레드가 끝날 때까지 기다리고 종료하는 메서드
'개발공부 > C++' 카테고리의 다른 글
[C++]Cstring double로 변경 (0) | 2023.12.04 |
---|---|
[MFC] 버튼 활성화 비활성화 (0) | 2023.11.21 |
[C++] 멀티 쓰레드, 프로세스, 쓰레드 이해하기, C++ 예제 (0) | 2023.09.20 |
[C++] MFC 다이얼로그 중심에 원 그리기 (0) | 2023.09.20 |
[C++] <chrono> 헤더 사용하여 함수 실행시간 측정 (0) | 2023.09.19 |