소연의_개발일지
article thumbnail

쓰레드

 

쓰레드 구문

#include <thread>

 

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

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

 

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

 

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

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

giveme-happyending.tistory.com

 


 

전체 코드

 

함수 인클루드

#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: 값의 합처럼 다른 쓰레드가 끝날 때까지 기다리고 종료하는 메서드

 

profile

소연의_개발일지

@ssoyxon

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