소연의_개발일지
article thumbnail

1. 쓰레드

 

쓰레드 구문

<cpp />
#include <thread>

 

detach()는 결과값을 기다리지 않고 다음 코드를 실행시키는 함수이다.

join은 다른 쓰레드들이 끝날 때까지 기다려서 합친 후 실행시킨다.

 

쓰레드 개념을 공부하기 위해 따로 정리해 보았다. 

 

[C++] 멀티 쓰레드, 프로세스, 쓰레드 이해하기, C++ 예제

쓰레드 공부 공부 출처: 모두의 코드 C++ 쓰레드 https://modoocode.com/269 씹어먹는 C ++ - 모두의 코드 씹어먹는 C ++ - 작성일 : 2019-04-01 이 글은 115087 번 읽혔습니다. 에 대해 다룹니다. 안녕하세요 여러

giveme-happyending.tistory.com

 


 

2. 전체 코드

 

함수 인클루드

<cpp />
#include "Process.h" // 프로세스 정보를 가져옴 #include <chrono> // 크로노 함수 #include <thread> using namespace std; // std::를 축약해 준다. using namespace chrono; // std::chrono::를 축약해 준다.

 

<cpp />
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; }

 

2.1. 코드 소요시간 측정 코드 만들기

실행시간을 측정하기 위해 기존 이미지 크기를 크게 변경시킨다.(소요시간이 너무 짧지 않게 하기 위해)

 

<cpp />
auto start = system_clock::now(); // 시작시간 // 여기에 측정할 코드가 들어갑니다. auto end = system_colok::now(); // 끝 시간 auto millisec = duration_cast<milliseconds>(end - start); cout << milliseconds.count() << endl;

가운데에 측정할 코드가 들어간다. 

 


2.2. 이미지 사이즈 조정 및 영역분할

 

<cpp />
// 전체 영역을 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개의 동일한 영역으로 나뉘게 된다.

<cpp />
+-----+-----+ | 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분할 했을 때 좌표만 다름을 확인할 수 있다.


2.3. 쓰레드 사용

 

함수 위에 thread를 include 시킨다.

(원래는 헤더 파일에 선언하는게 올바름)

 

 

 

 

헤더에 선언해주고

 

쓰레드 버튼을 클릭했을 때 타는 코드를 마저 작성해 준다.

<cpp />
#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 함수는 이 코드들 바로 밑에 작성한다.

 

<cpp />
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 파일에서 수정한 코드. 

<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보다 큰 수를 센다.


<cpp />
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를 써야 한다.

- 쓰레드는 리턴값이 없기 때문에 구조체든 변수든, 주어질 때 함수를 통해 얻어내야 한다.

<cpp />
cout << nRet << "\t" << millisec.count()*0.001 << "sec" << endl;

- 쓰레드 시간 결과값을 얻기 위해  측정 시간값을 동일게 작성해주고 출력한다.

 

일반 실행은 2초 걸렸는데, 쓰레드를 사용하니 그 4분의 1값이 나왔다.

 

 


2.4. detach 와 join 의 차이점

  • detach: 이미지 저장할 때 처럼 기다릴 필요가 없을 때, 끝날때까지 기다리지 않고 종료하는 메서드
  • join: 값의 합처럼 다른 쓰레드가 끝날 때까지 기다리고 종료하는 메서드

 

profile

소연의_개발일지

@ssoyxon

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!