01.05

신승빈·2023년 1월 5일
0

KGCA 수업

목록 보기
89/128

Thread

CreateThread

Windows의 thread 생성 함수
POSIX는 pthread_create, cpp는 thread로 생성

Reference : https://modoocode.com/269
HANDLE WINAPI CreateThread(
    _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
    _In_ SIZE_T dwStackSize,
    _In_ LPTHREAD_START_ROUTINE lpStartAddress,		// 함수포인터
    _In_opt_ __drv_aliasesMem LPVOID lpParameter,	// 매개변수
    _In_ DWORD dwCreationFlags,
    _Out_opt_ LPDWORD lpThreadId					// Thread ID
    );
typedef DWORD (WINAPI *PTHREAD_START_ROUTINE)(
    LPVOID lpThreadParameter
    );
typedef PTHREAD_START_ROUTINE LPTHREAD_START_ROUTINE;
DWORD WINAPI ThreadStartFunction(LPVOID lpThreadParameter)

위 형식의 함수 정의 후 함수포인터로 CreateThread에 넘기면 Thread생성
Return값인 Handle과 반환된 ThreadID를 통해 Thread 제어 가능
이 때 Return된 Handle은 CloseHandle()을 통해 닫아줘야 함

CPP와 POSIX thread의 차이

CPP11부터 추가된 Thread는 호환성이 떨어질 수 있다고 함

Reference : https://stackoverflow.com/a/13135425/8106257

Time sharing

CPU 스케줄링 기법
대표적으로 Round Robin이 있음

Thread의 점유

Thread가 CPU를 점유하면 Thread Quantum동안 CPU 점유를 보장

Reference : https://xenostudy.tistory.com/90

재밌는 점은 Thread에서 유휴시간이 없으면 해당 Thread에서 CPU를 독점하여 다른 작업을 할 수 없다는 이야기가 있음

Reference : https://drdbg.com/86

테스트 결과 맞기도 하고 틀리기도 한 말인듯

DWORD WINAPI Thread1(LPVOID lpThreadParameter)
{
	while (1)
	{
		//Sleep(1);
		printf("1");
	}
}

DWORD WINAPI Thread2(LPVOID lpThreadParameter)
{
	while (1)
	{
		//Sleep(1);
		printf("2");
	}
}

int main()
{
	//SetProcessAffinityMask(GetCurrentProcess(), 1);

	HANDLE handle1 = CreateThread(0, 0, Thread1, 0, 0, 0);
	SetThreadAffinityMask(handle1, 1);	// Thread의 수행 core를 제한하는 함수

	HANDLE handle2 = CreateThread(0, 0, Thread2, 0, 0, 0);
	SetThreadAffinityMask(handle2, 1);

	while (1)
	{
		//Sleep(1);
		int i = 0;
	}
}

위 코드처럼 sleep같은 유휴 시간 없이 각 Thread가 출력을 하는 프로그램을 실행 시

이처럼 단일 Thread의 출력이 꽤 길게 유지되는 것을 확인할 수 있음

Sleep을 풀 경우

느리지만 전반적으로 꽤 균일하게 Thread가 수행되는 것을 확인할 수 있음

찾아보니 Thread의 context switching이 꼭 유휴시간이나 interrupt에서만 발생하는 것은 안닌듯 함

Reference : https://kyuri107.tistory.com/entry/%EC%8A%A4%EB%A0%88%EB%93%9C-%EC%BB%A8%ED%85%8D%EC%8A%A4%ED%8A%B8-%EC%8A%A4%EC%9C%84%EC%B9%98-%EC%8B%9C%EC%A0%90-%EC%95%8C-%EC%88%98-%EC%9E%88%EB%8A%94-%EB%B0%A9%EB%B2%95

위 자료에 의하면 IO interrupt, Thread API(Sleep, WaitForSingleObject 등) 외에도 메모리에 Memory Page Fault가 발생하거나 해당 Thread의 Time Slice(Thread Quantum)을 다 할 경우 Context switching이 발생한다고 함

다만, 이 경우는 코드상으로는 확인이 어렵고, 실행 시점을 짐작하기도 어렵기 때문에 명시적으로 Sleep같은 함수를 이용하여 Context Switching을 수행하는 것이 균등한 프로그램 수행을 유도할 수 있을 듯

Sleep의 경우 0과 1에 차이가 있다고 함

Sleep(N) : N밀리초만큼 시간 조각을 포기
Sleep(0) : 같은 우선순위의 쓰레드에게 시간 조각을 양도
Sleep(1) : 다른 쓰레드에게 시간 조각을 양도

Reference : https://valueelectronic.tistory.com/236
Reference : https://developstudy.tistory.com/55

하이퍼스레딩

프로세스의 스레딩과는 다른 개념인 듯 한데 단순히 코어를 늘리지 않아도 실제로 성능 향상이 있는 듯

Reference : https://donghoson.tistory.com/20

메모리 가시성, 메모리 장벽, 동기화 객체

Multi Thread를 할 경우 메모리 가시성과 메모리 장벽은 중요한 개념
나중에 한번 읽어보면 좋을 듯

Reference : https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&blogId=jjoommnn&logNo=130037479493
Reference : https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&blogId=jjoommnn&logNo=130037479493

TCP Recv()

TCP의 경우 Sliding Window를 통해서 데이터를 순차적으로 전달하고, Protocol Header에서도 FIN Flag를 통해 모든 데이터의 전송이 완료되었는지 여부를 확인할 수 있음

Reference : https://ko.wikipedia.org/wiki/%EC%A0%84%EC%86%A1_%EC%A0%9C%EC%96%B4_%ED%94%84%EB%A1%9C%ED%86%A0%EC%BD%9C
Reference : https://ko.wikipedia.org/wiki/%EC%8A%AC%EB%9D%BC%EC%9D%B4%EB%94%A9_%EC%9C%88%EB%8F%84

찾아보니 Socket의 Read Buffer에 저장되는 값은 TCP가 모든 데이터를 전송한 후 완결된 Data가 아닌 TCP Packet이 전송한 데이터 단위로 Read Buffer에 작성이 되는 구조인 듯

이 때문에 Sender에서 보낸 데이터의 크기가 Receiver가 읽고자 하는 Buffer의 크기보다 더 작은 크기의 또는 MTU 1500보다 작은 데이터를 보내더라도 네트워크 환경에 따라 데이터는 분할이 될 수 있고 Receiver는 분할된 데이터를 받을 때마다 recv()함수를 호출할 수 있음

Reference : https://kldp.org/node/46190
Reference : https://kldp.org/node/20254

TCP 네트워크 스택

Reference : https://d2.naver.com/helloworld/47667
profile
이상을 길잡이 삼아 로망을 추구합니다.

0개의 댓글