'소프트웨어'에 해당되는 글 190건

  1. 2007.05.14 Winsock 프로그래밍 연습 - 12 : 스레드를 이용한 에코 채팅 프로그램 6
  2. 2007.05.14 Winsock 프로그래밍 연습 - 11 : Microsoft ICMP API 를 이용한 Ping 프로그램
  3. 2007.05.06 Winsock 프로그래밍 연습 - 10 : ICMP, raw 소켓(socket)을 이용한 ping 프로그램
  4. 2007.05.02 스레드 프로그래밍 연습 - 3 : 스레드 동시 수행 1
  5. 2007.05.02 스레드 프로그래밍 연습 - 2 : 스레드 종료
  6. 2007.05.02 스레드 프로그래밍 연습 - 1 : 스레드 생성
  7. 2007.05.01 Winsock 프로그래밍 연습 - 9 : TCP 통신 에코 서버/클라이언트(Echo Server/Client) 연습
  8. 2007.04.30 멀티스레딩 교재(Multithreading Tutorial)
  9. 2007.04.28 Winsock 프로그래밍 연습 - 8 : WebMail POP3 Check
  10. 2007.04.28 Winsock 프로그래밍 연습 - 7 : GetHTTP
  11. 2007.04.27 Winsock 프로그래밍 연습 - 6 : hostinfo
  12. 2007.04.15 Winsock 프로그래밍 연습 - 5 : TCP 를 이용한 간단 클라이언트 서버 구현
  13. 2007.04.15 Winsock 프로그래밍 연습 - 4 : UDP 를 이용한 간단 클라이언트 서버 구현
  14. 2007.04.15 Winsock 프로그래밍 연습 - 3 : WSAStartup() 함수 예제
  15. 2007.04.15 Winsock 프로그래밍 연습 - 1 : 삽질
  16. 2007.04.14 Winsock 프로그래밍 연습 - 2 : 삽질
  17. 2007.03.10 C++ 언어 강의 노트 - 이론 / 실습 (기초 부터 끝까지)
  18. 2007.03.10 C 언어 강의 노트 - 실습 /이론 (기초부터 끝까지)
  19. 2007.03.10 [2005년 기말] 정리
  20. 2007.03.10 [2005년 기말 10번] 사전 정렬 - 문자열
  21. 2007.03.10 [2005년 기말 9번] 직육면체 도형 면적 / 부피 구하기 - 클래스 / 상속
  22. 2007.03.09 [2005년 기말 8번] 직원 급여 관리 - 이중 포인터 / 클래스 / 생성자 / inline / 동적 배열 생성
  23. 2007.03.09 [2005년 기말 7번] 자소 분석 - 문자열 제어
  24. 2007.03.09 [2005년 기말 6번] 성적 / 평점 / 등급 / 학점 계산 - 조건문
  25. 2007.03.09 [2005년 기말 5번] 표준편차 구하기 - 벡터 / 수학함수
  26. 2007.03.09 [2005년 기말 4번] 문자열 역순 출력 - 동적 할당 / 문자열 출력
  27. 2007.03.09 [2005년 기말 3번] 시간 계산 - 나머지 / 초 계산 / 시각
  28. 2007.03.09 [2005년 기말 2번] 문자열 출력 - 길이 비교
  29. 2007.03.09 [2005년 기말 1번] 성적 처리 - 동적 할당 / vector STL
  30. 2007.03.08 [2005년 중간] 정리
코드 설명
Thread 와 Socket Programming 어느 정도 배운 상태에서 나올수 있는 숙제입니다. Blocking 방식으로 스레드를 활용하여 여러명을 받아 메시지를 뿌리는 프로그램입니다. Mutex 을 활용하여 자원을 공유하여 수행합니다. 5명까지 받을수 있도록 하되 그 이상을 받으면 오류를 내는 프로그램 입니다.

참고 - 학교 과제용 코드 입니다. 그러니 이제 검색되므로 이 코드로는 채점되기 쉽지 않을 겁니다. 예제로 쓰시기 바랍니다.

받기
클라이언트


서버


실행화면
사용자 삽입 이미지

코드내용

Posted by 카켈
코드 출처
http://www.sockaddr.com/ExampleSourceCode.html

코드 받기
원본


수정된 코드


수정 내용
CPP 에 맞게 수정
한글화

주의 사항
원래 코드 파일인 C로는 컴파일되는 코드지만 CPP 로 하면 안되는 부분이 있습니다.
GetProcAddress 와 FreeLibrary 인 곳인데, 첫번째 인수가 struct HISTANCE 형이여야 합니다. C 코드는 그냥 void 포인터로 넣어도 문제가 없는데 CPP 에서는 형검사(타입 체킹)을 하기 때문에 문제가 발생합니다.

cannot convert parameter 1 from 'void *' to 'struct HINSTANCE__ *'

(HISTANCE)를 붙여서 강제 형변환(type-casting)을 하면 해결할수 있습니다.
GetProcAddress(hndlIcmp, "IcmpCreateFile"); -> GetProcAddress((HINSTANCE)hndlIcmp, "IcmpCreateFile");
FreeLibrary(hndlIcmp); -> FreeLibrary((HINSTANCE)hndlIcmp);

C 에서는 문제가 없습니다.

실행 화면
사용자 삽입 이미지

코드 내용

Posted by 카켈
원본 출처
http://www.sockaddr.com/ExampleSourceCode.html

코드 받기
- 수정


- 원본


코드 설명
콘솔창에 있는 ping 명령어를 직접 짜는 내용입니다.

실행 화면
사용자 삽입 이미지

코드 내용



추가 정보
rawsocket 정보 -- http://myhome.shinbiro.com/~eaglelee/rawsocket.txt
- 받아 보기


IP -- RFC 791
http://www.faqs.org/rfcs/rfc791.html

ICMP -- RFC 792
http://www.faqs.org/rfcs/rfc792.html



Posted by 카켈
코드 주소
http://kin.naver.com/db/detail.php?d1id=1&dir_id=10104&eid=9kk6jmd+YayvujZz7fRRjP8F0n0+o/Yc&qb=vrK3ubXl

코드 받기



수행 화면
사용자 삽입 이미지

코드 설명

병렬로 스레드를 수행할때 지연을 줬을때와 안 주었을때 그리고 함수로 순차 수행했을때 차이를 보여주는 예제 입니다.

코드 내용
/*
	저작권 표기
	네이버 지식인 : C 쓰레드 예제에서 에러가 납니다... 고수님들 도와주세요~~ 
	http://kin.naver.com/db/detail.php?d1id=1&dir_id=10104&eid=0bUQ1CpEfllQ+RSELTqBVtOEPnrGHJN/&qb=vrK3ubXl
	http://cakel.tistory.com

	교육용을 목적으로 자유롭게 배포 할수 있습니다.

	thread_dsp.cpp : 극한의 상황을 줘서 지연을 주었을때,
	안주었을 때의 스레드 처리 순서와 함수로 줬을때 처리 순서를 나타내는 프로그램입니다.
        WIN32 의 함수를 사용했습니다.


*/

#include <process.h>
#include <stdio.h>
#include <windows.h>
#include <conio.h>

void dsp1(void *param)
{	// 100 ms 를 단위로 한줄을 출력을 하는 함수 입니다.
	int i = 0;
	for (i=0; i<10; i++)
	{
		printf("[1]%d\n",i);
		Sleep(100);
	}

	printf("dsp1 수행 완료\n");

}

void dsp2(void *param)
{	// 100 ms 를 단위로 한줄을 출력을 하는 함수 입니다.
	int i = 0;
	for (i = 0; i< 10; i++)
	{
		printf("[2]%d\n", i);
		Sleep(100);
	}

	printf("dsp2 수행 완료\n");

}

void dsp3(void *param)
{	// delay 없이 한줄을 바로 출력하는 함수입니다.
	int i = 0;
	for (i=0; i<10; i++)
	{
		printf("[3]%d\n",i);
	}

	printf("dsp3 수행 완료\n");

}

void dsp4(void *param)
{	// delay 없이 한줄을 바로 출력하는 함수입니다.
	int i = 0;
	for (i = 0; i< 10; i++)
	{
		printf("[4]%d\n", i);
		}

	printf("dsp4 수행 완료\n");

}

int main()
{
	// 스레드의 주소를 담는 핸들을 선언합니다.
	HANDLE th1, th2;
	
	// Race condition(경쟁조건) 에서 수행합니다.
	printf("Sleep(100)을 쓰고 스레드 1, 2를 실행 합니다.\n");
	th1 = (HANDLE)_beginthread(dsp1, 0, NULL);
	th2 = (HANDLE)_beginthread(dsp2, 0, NULL);

	// 스레드 완료를 위해 무한으로 기다립니다.
	WaitForSingleObject(th1, INFINITE);
	WaitForSingleObject(th2, INFINITE);

	printf("아무키를 치면 다시 딜레이 없이 바로 스레드 3, 4를 실행합니다.\n");
	
	_getch();	// 아무키를 입력받습니다.

	// 원래는 1번과 2번 스레드가 동시에 일어나므로 느린 PC 에서는 같이 출력될수 있습니다.
	
	// 다시 스레드를 구동합니다.
	th1 = (HANDLE)_beginthread(dsp3, 0, NULL);
	th2 = (HANDLE)_beginthread(dsp4, 0, NULL);

	// 스레드 완료를 위해 무한으로 기다립니다.
	WaitForSingleObject(th1, INFINITE);
	WaitForSingleObject(th2, INFINITE);

	printf("아무키를 치면 하나의 Sleep(100)을 쓴 함수 1, 2(프로시져)로 수행합니다.\n");

	_getch();	// 아무키를 입력받습니다.

	// 단일 함수로써 dsp1(), dsp2() 함수로 호출합니다.
	// NULL 과 0 은 같은 역할을 합니다.
	dsp1(NULL);
	dsp2(0);

	printf("수행완료 프로그램 끝\n");

	return 0;

}
Posted by 카켈
코드 출처
http://www.warpspeed.com.au/cgi-bin/inf2html.cmd?..\html\book\Toolkt40\XPG4REF.INF+101
바로 받기




코드 개요
스레드를 강제 종료하는 내용과 스레드 수행 대기를 담았습니다.

실행 화면
사용자 삽입 이미지

코드 설명
/*
	스레드 프로그래밍 연습 - 2 : 스레드 강제 종료
	thread_end.cpp

	저작권
	http://www.warpspeed.com.au/cgi-bin/inf2html.cmd?..\html\book\Toolkt40\XPG4REF.INF+101 - 출처
	http:/cakel.tistory.com - 번역/수정
	교육용을 목적으로 자유롭게 배포가 가능합니다.

	스레드를 생성한 후 강제 종료하는 방법의 예를 기술한 문서입니다.

*/

#include <stdio.h>
#include <process.h>
#include <windows.h>

/*

+	_endthread() - 최근에 수행을 시작한 스레드가 강제종료 됩니다.
	_beginthread() 와 같이 씁니다. (WIN32용)
	 closethread() 와는 다른 방식으로 생성 되므로 closethread()
	 함수는 createthread() 와 맞춰 쓰시면 됩니다. (MFC용)
	 용도에 따라 다른 쓰레드 함수를 쓰시기 바랍니다.

*/

void hello(void *arg)
{
	int i = 0;
	while (i++ < 5)
	{
		printf("안녕하세요!\n");
		
	}
	_endthread();		// 여기 까지 실행하고 종료 됩니다.
	puts("hello 스레드가 여길 실행하지 못하고 종료 합니다."); 
	return;

}

void nice(void *arg)
{
	int i = 0;
	while (i++ < 5)
	{
		printf("반갑습니다!\n");

	}
	// 스레드가 명시된 곳 까지 수행할수 있습니다.
	puts("nice 스레드가 여기까지 수행하고 종료 합니다.");

	return;
}

int main(void)
{
	// 스레드의 수행 주소를 담은 핸들
	HANDLE thread_1, thread_2;

	// 스레드 활성화(Activation)
	thread_1 = (HANDLE) _beginthread(hello, 0, NULL);
	thread_2 = (HANDLE) _beginthread(nice, 0, NULL);

	// 메인 스레드인 main() 함수가 먼저 return 0 로 도달하면
	
	/* WaitForSingleObject(스레드 주소, 시간)
	 스레드가 다 실행 못하고 끝이 나므로 WaitForSingleObject
	 함수를 써서 해당 스레드가 끝날때 까지 기다려 줍니다.
	 시간이 INFINITE 는 예약어로써 무한대로 기다려준다는 뜻입니다.
	
	*/
	
	
	/* _beginthread 가 long 형으로 수행주소를 리턴 하지만
		WaitForSingleObject 에서 받는 스레드 주소는 void* 로 받기
		때문에 void* 형인 HANDLE 로 스레드 주소를 저장합니다.
		long* 주소를 void* 으로 형 변환합니다.
		void 포인터(void* ) 는 범용으로 주소를 형변환 하면 자유자재로
		받을수 있습니다. 대신 내 보낼때 특정 형식에 맞춰서 보내지 않으면
		얘기치 못한 오류가 생길수 있습니다. */

	WaitForSingleObject(thread_1, INFINITE);
	WaitForSingleObject(thread_2, INFINITE);

	printf("생성된 스레드가 종료 되었습니다.\n");

	return 0;

}
Posted by 카켈
코드 참조
http://keegan.tistory.com/144

코드 받기


실행 화면
사용자 삽입 이미지

컴파일시 주의
프로젝트 설정(Alt + F7)에서 코드 생성 항목에서 Use run time libary 에 Debug Multithreaded 로 맞춰 주시면 됩니다.

사용자 삽입 이미지



코드 설명
/*
	- 스레드 프로그래밍 연습 - 1 : 스레드 생성
	
	- 저작권 알림
	http://keegan.tistory.com/144 - 원본
	http://cakel.tistory.com - 수정
        교육용을 목적으로 자유롭게 쓰실수 있습니다.

	- 코드 : thread.cpp
        스레드 구현하여 각각 다른 글자들을 출력하는 내용입니다. 
	WIN32 C++ 용입니다.


*/

// #include <헤더>	      // 필요한 함수
#include <windows.h>		// Sleep()
#include <stdio.h>		// printf(), fprintf()
#include <process.h>		// _beginthread();
#include <conio.h>		// _getch()

// 전역 변수
int repeatA;
int repeatB;

void PrintA(void *pData);	// 첫번째 스레드
void PrintB(void *pData);	// 두번째 스레드

int main()
{
	int ch;

	// fprintf()
	// 입출력 스트림으로 해당 내용을 출력합니다.
	// stdout 이 화면이므로, printf 함수와 크게 다르지 않게 작동합니다.
	fprintf(stdout,"a, b 를 입력하면 출력하지 않습니다. 다른키를 누르면 종료합니다.\n");
	fprintf(stdout,"시작!\n");
	
	// _beginthread(함수명, 스택크기, 단일인수)
	// 스택크기는 0으로 하면 운영체제에서 알아서 넣어 줍니다.
	// 보통 void* 으로 세번째 인수를 쓰는데, 복잡하게 쓸수도 있습니다.
	
	// 스레드는 변수를 공유하면서도 프로세스내에서 병렬적으로 같이 실행할수 있는
	// 부프로그램입니다.
	_beginthread(PrintA, 0, NULL);
	_beginthread(PrintB, 0, NULL);

	repeatA = 1;
	repeatB = 1;

	while(1)
	{		
		// 키보드 입력을 받는 메인 스레드에서 Ctrl + C 눌르거나
		// 다른키를 눌러서 return 으로 버리면 이미 생성된 스레드도 동반 종료 합니다.

		// _getch() : getchar() 함수와 달리 엔터를 치지 않아도
		// 한번의 키보드 입력으로 들어온 값을 바로 버퍼에 들어가서
		// 실행이 완료되는 함수 입니다. conio.h 헤더를 이용합니다.


		ch = _getch();
		if (ch == 'a')
			repeatA = 0;

		else if(ch == 'b')
			repeatB = 0;

		else
			break;


	}

	return 1;

}

void PrintA(void* pData)
{
	while(repeatA)
	{printf(" a ");
	Sleep(1000);}
	return;

}

void PrintB(void *pData)
{
	while(repeatB)
	{printf(" b ");
	Sleep(1000);}
	return;

}
Posted by 카켈
  소개
  코드를 머리 속에서 최대한 외울려고 했는데, 서버에서 bind 부분이랑 accept 이 막혔습니다.
기존에 준 ref 코드와 다른 것은 오류를 최대한 막을려고 했기 때문에 ref 기반에서 출발했지만 약간 다릅니다. 하지만 거의 같습니다. 제가 했던 리포트랑 엄청 짧고 간편해졌습니다;

  이전 TCP 통신에서 응용한 것입니다.

  수행 화면
사용자 삽입 이미지

  코드 받기
수정된 코드


ref 코드(reference - 참조)


  코드 내용
Posted by 카켈
원본 주소 : http://www.computersciencelab.com/MultithreadingTut1.htm
예제 코드 :


저작권 표기 : 원 저작글은 computersciencelab.com 의 John Kopplin 님에게 있습니다. 번역에 대한 저작권은 딱히 존재 하지 않지만 퍼 가실때 1차와 2차(http://cakel.tistory.com) 출처를 표기 해주셨으면 합니다. 이 글은 교육용입니다. 배우는 입장이기 때문에 오탈자가 상당수 있습니다. 덧글이나 아래 있는 메일 주소로 이야기 해주시면 최대한 수정하도록 하겠습니다.

Copyright notice : This article is owned to John Koppin at computersciencelab.com and I don't claim any Korean translation copyrights, but if you scrap or copy, please note this site. If you look any errata, comment or mail below address. This article is study purporse only.

Multithreading Tutorial
멀티 스레딩 교재


Background
배경

When you run two programs on an operating system that offers memory protection, as Windows and UNIX/LINUX do, the two programs are executed as separate processes which means they are given separate address spaces. This means that when program #1 modifies the address 0x800A 1234 in its memory space, program #2 does NOT see any change in the contents of its memory at address 0x800A 1234. With simpler operating systems that cannot accomplish this separation of processes, a faulty program can bring down not only itself but the other programs running on that computer (including the operating system itself).

  두개의 프로그램을 윈도우즈나 리눅스/유닉스가 하는 메모리 보호를 하는 운영체제에서 동시에 쓸때, 그 프로그램들은 독립적인 메모리 주소를 가진 프로세스로 동작하게 됩니다. 이는 프로그램 #1이 0x800A 1234 에서 수정하고 있다면 프로그램 #2 는 0x800A 1234 주소가 변경 되었는지 알수가 없습니다. 단순한 운영체제에서는 이 프로세스를 구별 하지 못하여 잘못 만들어진 프로그램은 자신 뿐 아니라 (운영체제를 포함한) 다른 프로그램들도 못쓰게(날리게) 만들수 있습니다.

The ability to execute more than one process at a time is known as multi-processing. A process consists of a program (usually called the application) whose statements are performed in an independent memory area. There is a program counter that remembers which statement should be executed next and there is a stack which holds the arguments passed to functions as well as the variables local to functions and there is a heap which holds the remaining memory requirements of the program. The heap is used for the memory allocations that must persist longer than the lifetime of a single function. In the C language you use malloc to acquire memory from the heap and in C++ you use the new keyword.

한번에 여러 개의 프로세스를 한번에 수행하는 능력을 다중(멀티)-프로세싱이라고 알려져 있습니다. 한 프로세스는 독립적인 메모리 공간을 가진 프로그램를 이루고 있습니다.(보통 응용프로그램-어플리케이션이라고 합니다.) 다음 수행할 문장이 어디에 있을지 저장하는 곳인 프로그램 카운터가 존재하고, 로컬 변수가 위치하고 함수의 인수를 보관하는 스택이 있으며, 프로그램이 요구한 용량을 쓰고 남은 공간인 영역이 있습니다. 힙은 단일 함수의 수명 보다도 더 오랫동안 존재 해야 하는 메모리를 할당 받는데 사용합니다. C 언어에서는 malloc 함수로 힙에서 메모리를 얻고 C++ 에서는 new 키워드로 얻습니다.

Sometimes it is useful to arrange for two or more processes to work together to accomplish one goal. One situation where this is beneficial is where the computer's hardware offers multiple processors. In the old days this meant two sockets on the motherboard each populated with an expensive Xeon chip. Thanks to advances in VLSI integration, these two processor chips can now fit in a single package. Examples are Intel's "Core Duo" and AMD's "Athlon 64 X2". If you want to keep two microprocessors busy working on a single goal, you basically have to two choices:

가끔 두개 이상의 프로세스가 같이 일을 하여 목적을 이루는 것이 더 유용할 때가 있습니다. 예를 들면 여러 프로세서를 동시에 쓸수 있는 하드웨어가 그러하겠습니다. 예전에 이 이야기는 두개의 CPU 슬롯을 가진 메인보드에 존재했던 비싼 인텔 제온(Xeon) CPU칩이 동작하는 시스템에 관한 이야기였습니다. 하지만 VLSI 집적회로의 진보 덕에 요즘은 하나의 팩키지에 두개(이상)의 프로세서가 내장 될수 있습니다. 인텔의 코어 듀오(Intel Core Duo), AMD 의 애슬론(Athlon) 64 X2 가 대표적 예가 되겠네요. 당신은 두 마이크로프로세서를 바쁘게 작동하여 하나의 목적을 달성하기 위해서, 기본적으로 두가지 선택을 해야 합니다.
  1. design your program to use multiple processes (which usually means multiple programs), or
    프로그램을 다중프로세스를 사용할수 있게 설계 (보통 멀티 프로그램을 의미합니다.) 또는

  2. design your program to use multiple threads
    멀티 스레드를 쓰는 프로그램을 설계 합니다.

So what's a thread? A thread is another mechanism for splitting the workload into separate execution streams. A thread is lighter weight than a process. This means it offers less flexibility than a full blown process, but can be initiated faster because there is less for the operating system to set up. What's missing? The separate address space is what is missing. When a program consists of two or more threads, all the threads share a single memory space. If one thread modifies the contents of address 0x800A 1234, then all the other threads immediately see a change in the contents of their address 0x800A 1234. Furthermore, all the threads share a single heap. If one thread allocates (via malloc or new) all of the memory available in the heap, then attempts at additional allocations by the other threads will fail.

스레드가 무엇알까요? 스레드란 작업량을 별개의 실행 스트림으로 분리한 또 다른 메카니즘(작동구현방식)입니다. 스레드 프로세스보다 가볍습니다. 이는 전체 프로세스 보다 덜 유연하다는 의미가 되지만 운영체제가 실행을 준비하는데 덜 부담을 주기 때문에 더 빠르게 작동될수 있습니다. 뭔가 빠졌죠? 각각 구별된 주소공간이 빠진 것입니다. 하나의 스레드가 0x800A 1234 의 공간을 변경하면 다른 모든 스레드가 동시에 0X800A 1234 주소값이 변경 된것을 볼수 있습니다. 더 나아가, 모든 스레드가 단일 힙을 공유 합니다. 만약 하나의 스레드(malloc 이나 new 를 통해)가 가용 할수 있는 모든 메모리 공간을 점유 한다면, 다른 스레드가 추가 할당할려는 시도는  실패 할 것입니다.

But each thread is given its own stack. This means thread #1 can be calling FunctionWhichComputesALot() at the same time that thread #2 is calling FunctionWhichDrawsOnTheScreen(). Both of these functions were written in the same program. There is only one program. But there are independent threads of execution running through that program.

그러나 각 스레드는 그만의 스택을 가지고 있습니다. 이는 스레드 #1 이 FunctionWhichComputesALot() 이라는 함수를 부를때 FunctionWhichDrawsOnTheScreen() 이라는 함수를 호출 합니다. 이 둘 함수는 같은 프로그램 내에서 작성된 것입니다. 하지만 그 프로그램에서 수행하는 각각의 독립적인 수행 스레드들이 존재 합니다.

What's the advantage? Well, if your computer's hardware offers two processors, then two threads can run simultaneously. And even on a uni-processor, multi-threading can offer an advantage. Most programs can't perform very many statements before they need to access the hard disk. This is a very slow operation and hence the operating system puts the program to sleep during the wait. In fact, the operating system assigns the computer's hardware resources to somebody else's program during the wait. But if you have written a multi-threaded program, then when one of your threads stalls, your other threads can continue.

여기서 얻는 이익은 무엇일까요? 음... 만약, 컴퓨터 하드웨어가 두개의 프로세서를 제공하고 있다면  두개의 스레드가 동시에 동작할 수 있습니다. 그리고 단일 프로세서라도 멀티-스레딩으로 같은 이득을 제공 받을수 있습니다. 대부분 프로그램은 하드 디스크에 접근 할 필요가 있기 전까지는 아주 많은 명령을 수행할 수 없습니다. 이는 아주 느린 작업이고 따라서 운영체제는 프로그램을 기다리는 동안 중단(sleep) 시킵니다. 사실 운영체제는 컴퓨터 하드웨어를 다른 사용자의 프로그램이 기다리는 동안에 하드웨어 자원을 할당 합니다. 하지만 멀티-스레드 프로그램을 쓰는데, 스레드들 중 하나가 멈춘다 해도, 다른 스레드는 계속 동작 할수 있습니다.

The Jaeschke Magazine Articles
재스케 매거진 기사

One good way to learn any new programming concept is to study other people's code. You can find source code in magazine articles and posted on the Internet at sites such as codeproject.com . I came across some good examples of multi-threaded programs in two articles written for the C/C++ Users Journal by Rex Jaeschke. In the October 2005 issue Jaeschke wrote an article entitled "C++/CLI Threading: Part 1" and in the November 2005 issue he wrote his follow-up article entitled "C++/CLI Threading: Part 2". Unfortunately the C/C++ Users Journal magazine folded shortly after these articles appeared. But the original articles and Jaeschke's source code are still available at the following web sites:

다른 사람의 코드를 보면서 새로운 프로그래밍의 계념을 익히는 건 좋은 방법들 중 하나 입니다. codeproject.com 같은 인터넷에 게시된 글이나 기사에 쓰인 소스 코드를 찾을수 있을 겁니다. 전 렉스 재스케에서 발간하는 C/C++ 사용자 저널에서 본  멀티-스레드에 관한 두가지의 좋은 예제 코드를 접할수 있었습니다. 2005년 10원에 발간된 이 재스케 잡지 기사의 제목이 "C++/CLI 스레딩 : 1부" 그리고 2005년 11월에 이어서 발간한 "C++/CLI 스레딩 : 파트2" 입니다. 불행히도 C/C++ 사용자 저널 매거진은 이들 기사가 나온지 얼마 안되어서 절간 되었는데요, 하지만 아직 원글과 재스케의 소스 코드가 아래 웹 사이트에서 받을수 있습니다.

Part 1: http://www.ddj.com/dept/windows/184402018
Part 2: http://www.ddj.com/dept/windows/184402029

You'll notice that the content from the defunct C/C++ Users Journal has been integrated into the Dr. Dobb's Portal web site, which is associated with Dr. Dobb's Journal, another excellent programming magazine.

당신은 더 이상 볼수 없을거 같은 컨텐츠를 다른 휼륭한 프로그래밍 매거진인 Dr, Dobb's 의 저널의 포털 사이트에서 위 컨텐츠를 보시게 될텐데요, 이는 재스케가 그 사이트로 병합되어서 그렇습니다.

You might not be familiar with the notation C++/CLI. This stands for "C++ Common Language Infrastructure" and is a Microsoft invention. You're probably familiar with Java and C#, which are two languages that offer managed code where the operating system rather than the programmer is responsible for deallocating all memory allocations made from the heap. C++/CLI is Microsoft's proposal to add managed code to the C++ language.

C++/CLI 에 친숙하지 않을지도 모르겠네요. 이는 C++ 공용 언어 기반공간(C++ 커먼 랭귀지 인프라스트럭쳐) 의 약자로써 마이크로소프트가 창안했습니다. 아마 자바나 C# 같은 운영체제 보다 프로그래머가 힙에서 만들어진 메모리를 제거하는 것에 책임을 지고, 제공된 관리 코드를 사용하는 언어에 친숙 할 것입니다. C++/CLI 는 마이크로소프트가 C++ 언어에 관리하는 코드를 추가해서 제안한 것입니다.

I am not a fan of this approach so I wasn't very interested in Jaeschke's original source code. I am sure Java and C# are going to hang around but C++/CLI attempts to add so many new notations (and concepts) on top of C++, which is already a very complicated language, that I think this language will disappear.

저는 이 부분에 관심이 없어서 그런지 재스케의 원래 소스 코드에 크게 관심이 없습니다. 전 Java 나 C# 이가 이미 활성화 되어 있지만 C++/CLI 는 아주 복잡해진 새로운 상위 개념을 넣었기 때문에 전   언어가 사라질 거라 생각하고 있습니다.

But I still read the original C/C++ Users Journal article and thought Jaeschke had selected good examples of multi-threading. I especially liked how his example programs were short and yet displayed data corruption when run without the synchronization methods that are required for successful communication between threads. So I sat down and rewrote his programs in standard C++. This is what I am sharing with you now. The source code I present could also be written in standard C. In fact, that's easier than accomplishing it in C++ for a reason we will get to in just a minute.

하지만 전 아직 원 C/C++ 사용자 저널 기사를 읽고 있고 재스케가 멀티스레드에 관한 좋은 예제를 골랐다고 생각하고 있습니다. 전 특별히 짧고 스레드간의 필요한 동기화 방법이 요구하지 않고도 데이터가 문제 없이 표시되는 그의 예제를 좋아 합니다. 그래서 전 마음 먹고 그의 프로그램을 표준 C++ 언어로 다시 썼습니다. 이게 지금 제가 공유하고 있는 것입니다. 이 소스 코드는 다시 C 언어로 쓸수 있습니다. 사실은 C++ 로 완수 하는 것 보다 C 로 만드는게 쉽습니다. 그 이유는 좀 있다가 알려 드리죠.

This is probably the right time to read Jaeschke's original articles, since I don't plan to repeat his great explanations of multitasking, reentrancy, atomicity, etc. For example, I don't plan to explain how a program is given its first thread automatically and all additional threads must be created by explicit actions by the program (oops). The URLs where you can find Jaeschke's two articles are given above.

이것이 재스케의 원 기사를 제대로 보는 기회가 되겠습니다. 왜냐하면 전 그의 멀티태스킹, 리엔트런시(데이터의 변조 없이 수행하는 함수), 원자성(더 이상 파괴될수 없는 성질), 등에 관한 대단한 설명에 대해 다시 언급 하지 않을 테니깐요. 예를 들어 그의 프로그램에서 어떻게 첫번째 스레드가 자동으로 주어지는지 추가적 스레드가 프로그램에 의해 명백하게 만들어져야만 한다는 것을 (이런... 알려 드렸네;) 알려 드리지 않을 것입니다. 저 URL들이 재스케의 두가지 기사 방금 언급한 것에 대한 것에 대한 해답을 찾을 것입니다.

Creating Threads Under Windows
스레드를 윈도우즈 하에서 생성하기

It is unfortunate that the C++ language didn't standardize the method for creating threads. Therefore various compiler vendors invented their own solutions. If you are writing a program to run under Windows then you will want to use the WIN32 API to create your threads. This is what I will demonstrate. The WIN32 API offers the following function to create a new thread:

불행히도 C++ 언어에서는 스레드를 생성하는 것에 대한 표준화가 이루어 지지 않았습니다. 그래서 다양한 컴파일러에서 그들만의 사용책을 제시하고 있는데요, 만약 사용자가 윈도우즈 하에서 프로그램을 돌리고 싶으시다면 WIN32 API로 스레드를 만들어하고 싶어 할 것입니다. 이게 제가 보여 드리고 싶은 것입니다. WIN32 API 는 아래 함수로 새 스레드를 생성하게 해줍니다.
 
uintptr_t _beginthread( 

    void( __cdecl *start_address )( void * ), unsigned stack_size, void *arglist 


);
 

This function signature might look intimidating, but using it is easy. The _beginthread() function takes 3 passed parameters. The first is the name of the function which you want the new thread to begin executing. This is called the thread's entry-point-function. You get to write this function, and the only requirements are that it take a single passed parameter (of type void*) and that it returns nothing. That is what is meant by the function signature:

이 함수 서명자는 웬지 보면 무섭습니다 하지만 사용한건 쉬워요. _beginthread() 함수는 3가지 인수를 가집니다. 첫번째 함수의 인자는 새로운 스레드가 시작되기를 원하는 함수의 이름입니다. 이를 가리켜 스레드의 진입-점-함수(엔트리-포인트-함수)라 불립니다. 프로그래머는 이 함수를 쓰되 그 함수는 void* 형식을 인수를 가지는 함수여야 합니다, 그리고 아무 것도 되돌리지(리턴) 않구요. 이 것은 함수 서명자로 의미하는 바입니다.
 
    void( __cdecl *start_address )( void * ),
 

The second passed parameter to the _beginthread() function is a requested stack size for the new thread (remember, each thread gets its own stack). However I always set this parameter to 0 which forces the Windows operating system to select the stack size for me, and I haven't had any problems with this approach. The final passed parameter to the _beginthread() function is the single parameter you want passed to the entry-point-function. This will be made clear by the following example program.

 _beginthread() 함수의 두번째 파라메터(인수)는 새 스레드가 필요한 스택 메모리의 크기 입니다.(각각의 스레드는 그만의 스택이 필요하다는 사실을 잊지 마세요). 하지만 전 항상 그 파라메터 값을 0으로 맟추는데, 이렇게 하면 윈도우즈 운영체제가 저를 위해 자동으로 선택하라고 설정합니다. 이걸로 전 어떤 문제를 받지 않았습니다. _beginthread() 함수의 세번째 인수는 단일 진입-점-함수에 넣고 싶은 단일 파라메터(인수)값입니다. 다음 예제에서 확실해질 것입니다.
 
#include <stdio.h>
#include <windows.h>
#include <process.h>         // needed for _beginthread()- 필요합니다.
 
void  silly( void * );       // function prototype 함수의 프로토 타입

int main()
{
    // Our program's first thread starts in the main() function.
    // 우리의 프로그램의 첫번째 스레드는 main() 함수에서 시작 할 겁니다. 

    printf( "Now in the main() function.\n" );

    // Let's now create our second thread and ask it to start
    // in the silly() function.
    // 우리의 두번째 스레드가 생성하고, silly() 함수 내에서 그 스레드에서
    // 시작할지 묻습니다. 

    _beginthread( silly, 0, (void*)12 );

    // From here on there are two separate threads executing
    // our one program.
    // 여기서 부터는 두개의 스레드가 한 프로그램에서 동작하게 됩니다. 

    // This main thread can call the silly() function if it wants to.
    // 함수가 원한다면 메인 스레드에서 silly()를 부를 수 있습니다.  

    silly( (void*)-5 );

    Sleep( 100 );
}

void  silly( void *arg )
{
    printf( "The silly() function was passed %d\n", (INT_PTR)arg ) ;
}
 

Go ahead and compile this program. Simply request a WIN32 Console Program from Visual C++ .NET 2003's New Project Wizard and then "Add a New item" which is a C++ source file (.CPP file) in which you place the statements I have shown. I am providing Visual C++ .NET 2003 workspaces for Jaeschke's (modified) programs but you need to know the key to starting a multi-threaded program from scratch: you must remember to perform one modification to the default project properties that the New Project Wizard gives you. Namely, you must open up the Project Properties dialog (select "Project" from the main Visual C++ menu and then select "Properties").

  지금 이 프로그램을 컴파일해보세요. 간단하게 비주얼 C++ .NET 2003 에서 Win32 콘솔 프로그램을 요구하여 새 프로젝트 마법사를 여시고, 제가 보여준 코드를 넣을수  있는 C++ 소스(.CPP)를 추가하는 "새 항목 추가"를 고르세요. 전 재스케의 (수정된) 프로그램을 위한 비주얼 C++ .NET 2003 작업공간(워크스페이스)를 제공합니다. 하지만 다중(멀티)-스레드 프로그래밍을 시작 할때 한가지 알아야 할 기본을 알아 두셔야 할 필요가 있습니다 : 당신은 반드시 새 프로젝트 마법사가 제공하는 기본 프로젝트 설정에서 하나를 변경해야 합니다. 다시 말해 프로젝트 설정 대화상자(비주얼 C++ 메뉴에서 "프로젝트 - 등록정보" 을 눌러 주세요)

In the left hand column of this dialog you will see a tree view control named "Configuration Properties" with the main sub-nodes labeled "C/C++", "Linker", etc. Double-click on the "C/C++" node to open this entry up. Then click on "Code Generation". In the right hand area of the Project Properties dialog you will now see listed "Runtime Library". This defaults to "Single Threaded Debug (/MLd)". [The notation /MLd indicates that this choice can be accomplished from the compiler command line using the /MLd switch.] You need to click on this entry to observe a drop-down list control where you must select Multi-threaded Debug (/MTd). If you forget to do this, your program won't compile and the error message will complain about the _beginthread() identifier.

대화 상자의 왼쪽 열에서 사용자는 "설정 등록정보" 제어 트리뷰(메뉴를 나무 가지 처럼 볼수 있게 해둔 형태)에서 메인 서브-항목(노드)에 "C/C++", "Linker", 등이라 적혀 있는 것을 볼텐데요. "C/C++" 을 더블클릭하세면 새부 항목이 열립니다. 항목에 보시면 "코드 생성(Code Generation)"이라는 항목이 있습니다. 오른쪽 영역의 프로젝트 등록정보에서 당신은 "런타임 라이브러리"(실행중 참고항목, Runtime Library)". 목록을 보게 될 것입니다. 기본 설정으로 "단일 스레드형의 디버그(/MLd)", [/MLd 라고 적힌 것은 컴파일러가 /MLd 스위치를 써서 컴파일을 완수 할 것이라고 언급 하는 것입니다]. 당신은 아래로-내림(드롭-다운)메뉴를 눌러, 반드시 다중(멀티)-스레디형의 디버그(/MTd). 라고 선택 하셔야 합니다. 이걸 까먹는다면 프로그램은 컴파일 되지 않고 _beginthread() 식별자에 대한 오류 메시지로 짜증 낼 겁니다.

A very interesting thing happens if you comment out the call to the Sleep() function seen in this example program. Without the Sleep() statement the program's output will probably only show a single call to the silly() function with the passed argument -5. This is because the program's process terminates as soon as the main thread reaches the end of the main() function and this may occur before the operating system has had the opportunity to create the other thread for this process.

아주 흥미로운 것은 이 예제에 보여진 Sleep() 함수가 어떻게 될아갈지에 관한 것입니다. Sleep() 함수 없이 프로그램의 출력은 아마 인수가 5씩 감소된 단일(1번) silly() 함수가 호출 될 것입니다. 이는 아마도 프로그램의 프로세스가 메인 스레드에서 생성하는 추가 스레드가 운영체제에서 생성하기 전에 이미 main() 함수의 끝에 다다라서 생성할 기회도 얻기 전에 종료 되어서 그럴 것입니다.

This is one of the discrepancies from what Jaeschke says concerning C++/CLI. Evidently in C++/CLI each thread has an independent lifetime and the overall process (which is the container for all the threads) persists until the last thread has decided to die. Not so for straight C++ WIN32 programs: the process dies when the primary thread (the one that started in the main function) dies. The death of this thread means the death of all the other threads.

재스케는 이 어긋난 프로그램에 대한 설명으로 C++/CLI 에 관하여 이야기 합니다. 결국 C++/CLI 에 있는 각 쓰래드는 독립적인 실행 시간을 가지고 있고 마지막에 남은 프로세서가 끝나기를 결정할때 까지 존재하는 전반적인 프로세스를 가지고 있다는 이야기가 됩니다.(다시 말해 모든 스레드를 가지는 컨테이너 - 담는 저장공간). C++ WIN32 프로그램에만 국한된 것이 아닙니다 : 첫 프로세스(메인 함수에서 시작하는 스레드)가 끝나 버리면 프로세스가 끝나 버립니다. 이 프로세스의 종료는 다른 프로세스들의 종료들 또한 의미 합니다.

Using a C++ Member Function as the Thread's Entry-Point-Function
C++ 맴버 함수를 스레드의 진입-점-함수로 사용하기

The example program I just listed really isn't a C++ program because it doesn't use any classes. It is just a C language program. The WIN32 API was really designed for the C language and when you employ it with C++ programs you sometimes run into difficulties. Such as this difficulty: "How can I employ a class member function (a.k.a. an instance function) as the thread's entry-point-function?"

이 예제는 클레스를 전혀 쓰지 않기 때문에 C++ 프로그램에만 국한된게 아닙니다. 그냥 C 프로그램입니다. WIN32 API 가 정말로 C 에 맞게 설계 되었기 있어서 C++ 언어를 실행하게 하면 몇가지 어려움에 봉착하게 됩니다. "어떻게 클래스 맴버(인스턴스 함수)들을 스레드의 진입-점-함수(엔트리-포인트-펑션)로 바꿀수 있을까?

If you are rusty on your C++, let me remind you of the problem. Every C++ member function has a hidden first passed parameter known as the this parameter. Via the this parameter, the function knows which instance of the class to operate upon. Because you never see these this parameters, it is easy to forget they exist.

당신이 C++ 에 대해 어느 정도 아신다면 이 문제를 기억하세요. 모든 C++ 맴버 함수는 숨겨진 첫번째 파라메터로 this 로 알려진 포인터가 있습니다. 이 파라메터를 통해서 맴버함수(인스턴스)는 어느 클레스 위에서 동작하는지 알수 있습니다. 이 파라메터는 볼수 없으니깐 그들의 존재는 잘 잊혀 집니다.

Now let's again consider the _beginthread() function which allows us to specify an arbitrary entry-point-function for our new thread. This entry-point-function must accept a single void* passed param. Aye, there's the rub. The function signature required by _beginthread() does not allow the hidden this parameter and hence a C++ member function cannot be directly activated by _beginthread().

자 다시 새 스레드를 위해 임의의 진입-점-함수를 명시하는것을 허용해주는  _beginthread() 함수를 생각해 봅시다. 이 진입-점-함수는 반드시 단일 void*  파라메터를 받아 줘야 합니다. 예 알지요, 거기에는 약간 수정해야 할게 있죠. _beginthread() 가 필요로 하는 함수 서명자에는 숨겨진 this 를 허용 하지 않으니깐 C++ 맴버 함수는 _beginthread() 함수로 바로 활성화 될수 없습니다.

We would be in a bind were it not for the facts that C and C++ are incredibly expressive languages (famously allowing you the freedom to shoot yourself in the foot) and the additional fact that _beginthread() does allow us to specify an arbitrary passed parameter to the entry-point-function.

우리는 C 와 C++ 언어가 표현력이 아주 풍부한 언어라는 사실이 아니였다면 곤경에 처했겠습니다.(유명하게도 직접 발을 차고 일어설 자유를 주죠.) 그리고 _beginthread() 함수는 임의의 진입-점-함수를 가리키는 포인터를 쓸수 있다는 사실이 존재합니다.

So we use a two-step procedure to accomplish our goal: we ask _beginthread() to employ a static class member function (which, unlike an instance function, lacks the hidden this parameter) and we send this static class function the hidden this pointer as a void*. The static class function knows to convert the void* parameter to a pointer to a class instance. Voila! We now know which instance of the class should call the real entry-point-function and this call completes the 2 step process. The relevant code (from Jaeschke's modified Part 1 Listing 1 program) is shown below:

그래서 우리는 2단계의 과정으로 우리의 목적을 완수 할 것입니다. 우선 _beginthread() 함수를 정적(스태틱) 클래스 맴버 함수로 둡니다(이건, 인스턴스 - 맴버, 함수와 다르게 숨겨진 this 파라메터가 없습니다) 그리고 이 정적 클래스 함수에게 this 포인터를 void* 로 (인수를) 보냅니다. 그 정적 클래스 함수는 void* 파라메터를 클래스 인스턴스를 가리키는 포인터로 바꿀 줄 앎니다.(자신이 그 클래스의 맴버에 존재하기 때문입니다.) 보세요(보일라~!) 우리는 어느 클래스의 인스턴스(맴버)가 진짜 진임-점-함수를 부를수 있는지 알게 되었고 이제 2단계 작업이 완료 되었습니다. 관련 코드(1부 목록 1 프로그램을 수정한 재스케의 코드)가 아래 있습니다.

 
class ThreadX

{
public:

  // In C++ you must employ a free (C) function or a static
  // class member function as the thread entry-point-function.
  // C++ 에서는 여유 있는 C 함수나 정적 클래스 맴버 함수를 쓰래드 진입-점-함수의
  // 클래스 맴버로 써야 합니다.

  static unsigned __stdcall ThreadStaticEntryPoint(void * pThis)
  {
      ThreadX * pthX = (ThreadX*)pThis;   // the tricky cast - 꽁수 변환
      pthX->ThreadEntryPoint();    // now call the true entry-point-function
                            	   // 이게 진짜 진입-점-함수

      // A thread terminates automatically if it completes execution,
      // or it can terminate itself with a call to _endthread().
      // 쓰래드는 자동으로 수행이 완료 되면 종료 됩니다. 아니면 _endthread() 자신이
      // 함수를 호출 합니다.

      return 1;          // the thread exit code - 스레드 종료 코드
  }

  void ThreadEntryPoint()
  {
     // This is the desired entry-point-function but to get
     // here we have to use a 2 step procedure involving
     // the ThreadStaticEntryPoint() function.
     // 이 진입-점-함수는 우리가 요구하는 거시만 ThreadStaticEntryPoint()를 수행하기
     // 위해 2단계의 작업이 필요 합니다.
  }
}

Then in the main() function we get the two step process started as shown below:

main() 함수는 아래 보여주는 거 처럼 2단계 작업이 필요 합니다.
 
hth1 = (HANDLE)_beginthreadex( NULL,         // security - 보안
                      0,            // stack size - 스택 크기
                      ThreadX::ThreadStaticEntryPoint,
			// entry-point-function - 진입-점-함수
                      o1,
			// arg list holding the "this" pointer -
			// this- 포인터의 인수
                      CREATE_SUSPENDED,
			// so we can later call ResumeThread()
			// 우리는 이후에 ResumeThread() 함수라 부를 겁니다.
                      &uiThread1ID );
 

Notice that I am using _beginthreadex() rather than _beginthread() to create my thread. The "ex" stands for "extended" which means this version offers additional capability not available with _beginthread(). This is typical of Microsoft's WIN32 API: when shortcomings were identified more powerful augmented techniques were introduced. One of these new extended capabilities is that the _beginthreadex() function allows me to create but not actually start my thread. I elect this choice merely so that my program better matches Jaeschke's C++/CLI code. Furthermore, _beginthreadex() allows the entry-point-function to return an unsigned value and this is handy for reporting status back to the thread creator. The thread's creator can access this status by calling GetExitCodeThread(). This is all demonstrated in the "Part 1 Listing 1" program I provide (the name comes from Jaeschke's magazine article).

제가 _beginthread() 대신에 _beginthreadex() 함수로 불러 쓰는 것을 보시기 바랍니다. "ex" 는 "extended - 확장의 줄임말입니다" 이는 이 버전은 _beginthread() 함수에서 받을수 없는 추가적인 기능을 가지고 있습니다. 이 함수는 WIN32 API 의 전형적인 함수 입니다 : 단점이 확인 되면 더 강력한 인수를 가진 기술이 소개 됩니다. 이 확장된 기능들중 하나인 _beginthreadex() 함수는 만드는데는 가능하지만 실제 내 스레드를 수행하게는 해주지 않습니다. 전 이 선택을 해야 재스케의 C++/CLI 코드 보다 더 잘 맞을거라 생각해서 골랐습니다. 더 나아가 _beginthreadex() 함수는 진입-점-함수를 부호 없는 값을 받을수 있게 하고 이는 스레드 생성자에게 성태 값을 받는걸 쉽게 해줍니다. 이 스레드 생성자는 이 상태를 GetExitThread() 함수로 접근할수 있습니다. 이 예제는 "1부 목록 1" 프로그램에 시연 되어 있습니다. (재스케의 잡지 기사에서 이름을 가져 왔습니다.)

At the end of the main() function you will see some statements which have no counterpart in Jaeschke's original program. This is because in C++/CLI the process continues until the last thread exits. That is, the threads have independent lifetimes. Hence Jaeschke's original code was designed to show that the primary thread could exit and not influence the other threads. However in C++ the process terminates when the primary thread exits and when the process terminates all its threads are then terminated. We force the primary thread (the thread that starts in the main() function) to wait upon the other two threads via the following statements:

main() 함수의 끝에 보게 될 재스케의 원 프로그램에 대응하지 않는 몇몇 부분을 보게 될 것입니다. 이 이유는 C++/CLI 프로세스는 남은 프로세스가 끝날때 까지 계속 수행하기 때문입니다. 이는 스레드는 각각의 독립적인 수행 시간을 가진다는 뜻입니다. 따라서 재스케의 원래 코드는 첫번째 스레드가 끝이 난다고 해서 다른 스레드에게 영향을 주지 않을 것이라고 설계 했습니다. 하지만 C++ 프로세스는 첫번째 스레드가 끝이 나면 다른 스레드도 덩달아 종료 됩니다. 우리는 강제로 첫번째 스레드(main() 함수에서 시작하는 스레드)를 종료 하기 위해 다음 구문들로 다른 두 스레드를 기다려야 합니다.
 
    WaitForSingleObject( hth1, INFINITE );
    WaitForSingleObject( hth2, INFINITE );
 
If you comment out these waits, the non-primary threads will never get a chance to run because the process will die when the primary thread reaches the end of the main() function.

만약 이들 기다리는 것에 대한 주석을 달지 않는다면, 첫번째 스레드가 main() 함수가 먼저 끝에 도달하면, 프로세스가 종료하게 되므로 두번째 스레드는 결코 실행할 기회를 가지지 못할 것입니다.

Synchronization Between Threads
스레드간 동기화

In the Part 1 Listing 1 program, the multiple threads don't interact with one another, and hence they cannot corrupt each other's data. The point of the Part 1 Listing 2 program is to demonstrate how this corruption comes about. This type of corruption is very difficult to debug and this makes multi-threaded programs very time consuming if you don't design them correctly. The key is to provide synchronization whenever shared data is accessed (either written or read).

1부 목록 1 프로그램에서 멀티(다중) 스레드는 다른 스레드에 영향을 주지 않는다고 했습니다. 따라서 그들은 서로 다른 자료를 변경하거나 망가뜨리지 못합니다. 1부 목록2 에서 어떻게 자료가 망가지는지 프로그램을 통해 보여 줄것입니다. 이런 변형은 디버그 하기 쉽지 않아 디자인을 제대로 하지 않은 멀티-스레드 프로그램은을 디버그하는데 시간을 오래 잡게 만들수 있습니다. 동기화의 가장 중요한 핵심은 언제든지 공유된 자료가 접근할수 있다는 것입니다.(쓰던지 읽던지)

A synchronization object is an object whose handle can be specified in one of the WIN32 wait functions such as WaitForSingleObject(). The synchronization objects provided by WIN32 are:

동기화 객체는 WIN32 에서 기다리는 함수인 WaitForSingleObject() 함수와 같이 미리 정의 된 객체 입니다. WIN32 에서 제공하는 동기화 객체는 아래와 같습니다.

  • event
  • mutex or critical section
  • semaphore
  • waitable timer

An event notifies one or more waiting threads that an event has occurred.

하나의 이벤트(사건) 하나 이상의 대기(위에 언급한 객체) 스레드에게 그 이벤트가 발생했다고 알려 줍니다.

A mutex can be owned by only one thread at a time, enabling threads to coordinate mutually exclusive access to a shared resource. The state of a mutex object is set to signaled when it is not owned by any thread, and to nonsignaled when it is owned by a thread. Only one thread at a time can own a mutex object, whose name comes from the fact that it is useful in coordinating mutually exclusive access to a shared resource.

mutex 는 한번에 한 스레드에 의해서만 소유 됩니다, 공유된 자료에 대해 서로 배타적(같이 쓸수는 있지만 쓸때는 그 스레드에서만 점유됨)으로 스레드에게 사용할수 있게 해줍니다. mutex 객체에 관한 상태는 어떤 스레드에게도 점유 되지 않았다면 신호를 보내고, 하나의 스레드에 의해 점유 되었다면 신호를 보내지 않습니다. 단 하나의 스레드만이 mutex 객체를 소유 할수 있습니다. mutex 라는 이름은 mutually exclusive 하게 자원을 접근하는게 유용하다는 사실에서 유래된 것입니다.

Critical section objects provide synchronization similar to that provided by mutex objects, except that critical section objects can be used only by the threads of a single process (hence they are lighter weight than a mutex). Like a mutex object, a critical section object can be owned by only one thread at a time, which makes it useful for protecting a shared resource from simultaneous access.

Critical section 객체들은 mutex 객체가 제공하는 거와 비슷한 동기화를 제공합니다. 대신 critical section 객체는 단일 프로세스의 스레드들에만 한정합니다.(다른 프로세스와는 공유가 되지 않습니다. 따라서 mutex 보다는 가볍습니디.) mutex 와 비슷하게 critical section 객체는 한번에 한 스레드에 의해서만 소유 될수 있습니다. 이는 동시에 접근하는 공유 자원에 대해 유용합니다.

There is no guarantee about the order in which threads will obtain ownership of the critical section, however, the operating system will be fair to all threads. Another difference between a mutex and a critical section is that if the critical section object is currently owned by another thread, EnterCriticalSection() waits indefinitely for ownership whereas WaitForSingleObject(), which is used with a mutex, allows you to specify a timeout.

스레드는 critical section 의 소유권을 가지는 것에 대한 규칙을 보증하지 않습니다 하지만, 운영체제는 모든 스레드에게 공정합니다. mutex 와 critical section 과 다른 점은 만약 다른 스레드에 의해 critical section 개체가 점유 당하면 EnterCriticalSection() 함수는 불명확(다른 스레드도 쓰기 때문)하게 소유권을 기다리는 반면 mutex 와 함께 쓰이는 WaitForSingleObject() 함수는 시간 경과를 기술하는 것을 허락해줍니다.

A semaphore maintains a count between zero and some maximum value, limiting the number of threads that are simultaneously accessing a shared resource.

semaphore 는 0 부터 최대값 까지 동시에 한 자원을 접근하는 (스레드의) 갯수를 관리합니다.

A waitable timer notifies one or more waiting threads that a specified time has arrived.

기다릴수 있는 타이머가 하나 이상의 타이머를 사용하여 스레드를 특정 시간이 올때 까지 기다리게 할수 있습니다.

This Part 1 Listing 2 program demonstrates the Critical Section synchronization object. Take a look at the source code now. Note that in the main() function we create 2 threads and ask them both to employ the same entry-point-function, namely the function called StartUp(). However because the two object instances (o1 and o2) have different values for the mover class data member, the two threads act completely different from each other.

이  1부 목록 2 프로그램은 Critical Section 동기화 객체를 시연합니다. 소스코드를 한번 보세요. main() 함수에서 우리는 2개의 스레드(o1 과 o2)를 만들고 그들에게 같은 StartUP() 이라는 진입-점-함수를 가지게 만듭니다. 하지만 각각 다른 값을 가진 이동 클레스 데이터  객체 맴버들을 가지고 있어서, 두 개의 스레드는 각각 서로 완전하게 다르게 행동합니다.

Because in one case isMover = true and in the other case isMover = false, one of the threads continually changes the Point object's x and y values while the other thread merely displays these values. But this is enough interaction that the program will display a bug if used without synchronization.

그 이유는 하나는 inMover = true 이지만 다른 하나는 inMover=false 입니다. 스레드들 중 하나는  지속적으로 포인트 객체(Point object)의 x 와 y 값을 바꾸는 동안 다른 스레드는 이들 값을 단순하게 표시만 합니다. 하지만 이는 충분하게도 동기화 없이 쓴다면 프로그램은 버그를 나타낼 반응을 가집니다.

Compile and run the program as I provide it to see the problem. Occasionally the print out of x and y values will show a discrepancy between the x and y values. When this happens the x value will be 1 larger than the y value. This happens because the thread that updates x and y was interrupted by the thread that displays the values between the moments when the x value was incremented and when the y value was incremented.

컴파일하고 제가 제공한 이 프로그램을 실행해 보세요. 가끔씩 x 와 y 값이 서로 어긋나게 보일 것입니다. x 가 y 값 보다 1 증가 할 때 발생 합니다. 이는 x 와 y 를 업데이트 시키는 스레드가 x 가 증가할때와 y 가 증가 할때 사이에 발생하는 값 표시 스레드에 의해서 중단 되기 때문입니다.

Now go to the top of the Main.cpp file and find the following statement:

이제 Main.cpp 상단 부분에 다음의 선언문을 넣습니다.
 
//#define WITH_SYNCHRONIZATION
 
Uncomment this statement (that is, remove the double slashes). Then re-compile and re-run the program. It now works perfectly. This one change activates all of the critical section statements in the program. I could have just as well used a mutex or a semaphore but the critical section is the most light-weight (hence fastest) synchronization object offered by Windows.

이 선언문에 주석 부분을 제거 합니다(더블 슬래시 부분을 제거하기). 그리고 다시 컴파일하고 프로그램을 재시작하면 잘 돌아 갑니다. 이 하나의 변화가 모든 critical section 선언문에 적용됩니다. 저는 mutex 나 semaphore 를 쓰긴 하는데 criticala section 이 윈도우즈에서 제공하는 가장 가볍게 돌아하는 동기화 객체(따라서 가장 빠릅니다)로써 자주 사용합니다.

The Producer/Consumer Paradigm
제공자/소비자 패러다임(체제)

One of the most common uses for a multi-threaded architecture is the familiar producer/consumer situation where there is one activity to create packets of stuff and another activity to receive and process those packets. The next example program comes from Jaeschke's Part 2 Listing 1 program.

한가지 사용자가 다중(멀티)-스레드 형태의 아키택쳐(구성체제)를 쓰는 가장 큰 이유중 하나는  생산자/소비자 상황에서 하나의 패킷을 보내면 바로 받을수 있는 활동 상황에 친숙하다는 것입니다. 다음 예제 프로그램은 재스케의 2부 1 목록 프로그램에서 가져 온 것입니다.

An instance of the CreateMessages class acts as the producer and an instance of the ProcessMessages class acts as the consumer. The producer creates exactly 5 messages and then commits suicide. The consumer is designed to live indefinitely, until commanded to die. The primary thread waits for the producer thread to die and then commands the consumer thread to die.

CreateMessages 클래스의 한 인스턴스(맴버함수)는 생산자 역할을 하고 ProcessMessages 클래스의 한 인스턴스 또한 소비자 역할을 합니다. 이 생산자는 정확히 5개의 메시지를 생성하고 그리고 소멸합니다. 소비자는 확실하지 않게(언제 생성하고 활동할지 불명확함) 존재하게 설계되어 있어서 소멸하기 전까지 계속 명령을 내립니다. 첫번째(1순위) 스레드가 생산자 스레드의 파괴를 기다리고 소비자 스레드에게 파괴를 명령하고 기다립니다.

The program has a single instance of the MessageBuffer class and this one instance is shared by both the producer and consumer threads. Via synchronization statements, this program guarantees that the consumer thread can't process the contents of the message buffer until the producer thread has put something there, and that the producer thread can't put another message there until the previous one has been consumed.

이 프로그램은 MessageBuffer 라는 클래스의 단일 인스턴스를 가지고 있고, 이 스레드는 생산자와 소비자 스레드를 포함하고 있습니다. 동기화 지시문으로, 이 프로그램은 소비자 스레드가 생산자 스레드가 무언가 넣기 전까지는 컨텐츠 가공을 수행할수 없습니다. 그리고 생산자 스레드는 이전 메시지가 쓰이기 전까지 새로운 메시지를 만들수 없습니다.

Since my Part 1 Listing 2 program demonstrates the critical section, I elected to employ a mutex in this Part 2 Listing 1 program. As with the Part 1 Listing 2 example program, if you simply compile and run the Part 2 Listing 1 program as I provide it, you will see that it has a bug. Whereas the producer creates the 5 following messages:

저의 1부 목록 2 프로그램에서 critical section 보여 주었으니 이번에는 mutex 를 사용해서 2부 목록 1 프로그램을 보여 드리겠습니다. 1부 목록 2 예제 프로그램에 보시는 봐와 같이 만약 2 부 목록 1 프로그램을 그냥 컴파일 해서 실행하시면, 버그를 보시게 될겁니다. 생산자 (스레드는) 5개의 아래 메시지를 생성합니다.

1111111111
2222222222
3333333333
4444444444
5555555555
the consumer receives the 5 following messages:

소비자 (스레드)는 5개의 메시지를 받게 됩니다.

1
2111111111
3222222222
4333333333
5444444444

There is clearly a synchronization problem: the consumer is getting access to the message buffer as soon as the producer has updated the first character of the new message. But the rest of the message buffer has not yet been updated.

이는 명백히 동기화 문제 입니다. 소비자는 바로 생산자가 첫번째 메시지의 첫문자를 생산하자 마자 접근을 해야 합니다. 하지만 남은 메시지의 버퍼는 아직 갱신 되지 않았습니다.

Now go to the top of the Main.cpp file and find the following statement:

다시 Main.cpp 로 돌아가서 다음 선언문을 찾아 가세요.

 
//#define WITH_SYNCHRONIZATION
 

Uncomment this statement (that is, remove the double slashes). Then re-compile and re-run the program. It now works perfectly.

위 선언문에서 주석 부분을 제거하세요(다시 말해, 더블 슬래시를 제거) 그리고 다시 컴파일 하시고 재실행 하면 잘 돌아 갑니다.

Between the English explanation in Jaeschke's original magazine article and all the comments I have put in my C++ source code, you should be able to follow the flow. The final comment I will make is that the GetExitCodeThread() function returns the special value 259 when the thread is still alive (and hence hasn't really exited). You can find the definition for this value in the WinBase header file:

재시케의 원 잡기 글과 제 C++ 소스 코드에 놓인 영문(한글) 주석문을 통해 여러분은 흐름을 이해 하실수 있습니다. 마지막 주석문으로 GetExitCodeThread() 함수에 관해 쓰겠는데요 이 함수는 스레드가 아직 살아 남으면(그리고 따라서 정말 종료하지 않았다면) 특별한 259 값을 반환 합니다. 사용자는 WinBase 헤더에서 이 값에 대한 정의를 발견 하실수 있습니다.
 
#define STILL_ACTIVE   STATUS_PENDING
 

where you find STATUS_PENDING defined in the WinNT.h header file:

STATUS_PENDING (상태 유보) 정의를 WinNT.h 헤더 파일에서 정의 된 것을 보실수 있습니다.
 
#define STATUS_PENDING    ((DWORD   )0x00000103L)
 
Note that 0x00000103 = 259.

16진수로 103 은 10진수로 259 입니다.

Thread Local Storage
스레드 지역 저장소(로컬 스로리지)

Jaeschke's Part 2 Listing 3 program demonstrates thread local storage. Thread local storage is memory that is accessible only to a single thread. At the start of this article I said that an operating system could initiate a new thread faster than it could initiate a new process because all threads share the same memory space (including the heap) and hence there is less that the operating system needs to set up when creating a new thread. But here is the exception to that rule. When you request thread local storage you are asking the operating system to erect a wall around certain memory locations in order that only a single one of the threads may access that memory.

재시케의 2부 목록 3 프로그램은 스레드 지역 저장소를 보여 줍니다. 스레드 지역 저장소는 단지 단일 스레드 만이 접근할 수 있는 저장공간입니다. 이 글의 시작 부분에서 운영체제에서는 새로운 스레드를 생성하는 것이 새 프로세스를 생성하는 거 보다 빠를수 있다고 했는데 그 이유는 모든 스레드는 같은 메모리공간(힙 공간을 포함)을 공유 하기 떄문이라고 말했습니다. 하지만 예외 규칙이 존재 합니다. 스레드 지역 저장소를 요청할려면 당신은 운영제제에게 단일 스레드만이 접근할수 있는 메모리 공간을 벽으로 보호해 달라고 요청하면 됩니다.

The C++ keyword which declares that a variable should employ thread local storage is __declspec(thread).

C++ 키워드로 스레드 지역 저장소를 선언하기를  __declspace(thread) 로 하시면 됩니다.

As with my other example programs, this one will display an obvious synchronization problem if you compile and run it unchanged. After you have seen the problem go to the top of the Main.cpp file and find the following statement:
제 다른 예제 프로그램에서 마찬가지로, 이 프로그램도 변경하지 않고 그냥 컴파일 해서 실행하면 명백한 동기화 문제가 생깁니다. 문제를 발견하면 다시 Main.cpp 코드로 가서 다시 수정하시기 바랍니다.
 
//#define WITH_SYNCHRONIZATION
 

Uncomment this statement (that is, remove the double slashes). Then
re-compile and re-run the program. It now works perfectly.


주석 부분을 제거하시고 (슬레쉬 두개를 지우세요) 다시 컴파일 하고 실행하시면 완벽하게
동작합니다.

Atomicity
원자성

Jaeschke's Part 2 Listing 4 program demonstrates the problem of atomicity which is the situation where an operation will fail if it is interrupted mid-way through. This usage of the word "atomic" relates back to the time when an atom was believed to be the smallest particle of matter and hence something that couldn't be further split. Assembly language statements are naturally atomic: they cannot be interrupted half-way through.

재스케의 2부 목록 4의 프로그램은 중간 단계에서 실행 중에 중단 되면 작업이 실패 되는 상황을 가진 원자성의 문제를 지닌 프로그램을 보여줍니다. 이를 "원자"와 연관된 단어로 사용되는데 이는 옛날 원자가 가장 작은 존재여서 더이상 쪼개어 지지 않는다고 믿어 와서 붙여진 단어입니다. 어셈블리 언어는 자연적으로 원자성을 가집니다 : 더 이상 반으로 언어를 쪼개지지 않습니다.(유사 명령어는 제외)

This is not true of high-level C or C++ statements. Whereas you might consider an update to a 64 bit variable to be an atomic operation, it actually isn't on 32 bit hardware. Microsoft's WIN32 API offers the InterlockedIncrement() function as the solution for this type of atomicity problem.

이 사실은 상위 C / C++ 언어에서는 적용되지 않습니다. 반면 64 비트 변수를 갱신한다고 생각하면 사실 32 비트 하드웨어에서 실제로 존재 하지 않으므로 원자성의 작업을 고려 해야 합니다. 마이크로소프트의 WIN32 API 는 interlockedincrement() 함수로 원자성에 관련된 문제에 대한 해법을 제시 합니다.

This example program could be rewritten to employ 64 bit integers (the LONGLONG data type) and the InterlockedIncrement64() function if it only needed to run under Windows 2003 Server. But, alas, Windows XP does not support InterlockedIncrement64(). Hence I was originally worried that I wouldn't be able to demonstrate an atomicity bug in a Windows XP program that dealt only with 32 bit integers.

이 예제 프로그램은 64 비트 정수(LONGLONG 형식)으로 다시 쓰여질수 있고 윈도우즈 2003 서버에서만 실행한다면 interlockedincrement64() 함수로 표현이 가능합니다. 하지만 슬프게도 Windows XP 는 interlockedincrement64() 함수를 지원하지 않습니다. 따라서 저는 처음부터 32 비트 정수만 다루는 윈도우즈 XP 에서 원자성에 관련된 문제를 표현하지 못할거 같아 걱정 했었습니다.

But, curiously, such a bug can be demonstrated as long as we employ the Debug mode settings in the Visual C++ .NET 2003 compiler rather than the Release mode settings. Therefore, you will notice that unlike the other example programs inside the .ZIP file that I distribute, this one is set for a Debug configuration.

그런데 신기하게도 비주얼 스튜디오 C++ .NET 2003 에서 발매(릴리즈) 모드 대신 디버그 모드로 수행하면 이 원자성에 관련된 문제를 표현할 수 있는 버그가 존재 합니다.

As with my other example programs, this one will display an obvious synchronization problem if you compile and run it unchanged. After you have seen the problem go to the top of the Main.cpp file and find the following statement:

다른 예제 프로그램들 처럼 이 프로그램도 코드를 수정하지 않으면  동기화 문제가 존재 합니다. Main.cpp 에 다음의 문장을 추가 합니다.
static boolinterlocked = false // change this to fix the problem
                               // 이 부분 바꾸어 문제를 해결합니다.
Change false to true and then re-compile and re-run the program. It now works perfectly because it is now employing InterlockedIncrement().
false 을 true 로 바꾸어 다시 컴파일 하고 실행하면 정상 동작합니다. 이유는 이제 interlockedincrement() 함수를 쓰기 떄문입니다.

The Example Programs
예제 프로그램

In order that other C++ programmers can experiment with these multithreaded examples I make available a .ZIP file holding five Visual C++ .NET 2003 workspaces for the Part 1 Listing 1, Part 1 Listing 2, Part 2 Listing 1, Part 2 Listing 3, and Part 2 Listing 4 programs from Jaeschke's original article (now translated to C++). Enjoy !

다른 C++ 프로그래머들이 다중(멀티) 스레드 예제를 실험 해보기 위해 1부 목록1, 2, 그리고 2부 목록 3, 4 으로 구성된 5 개의 비주얼 스튜디오 C++ .NET 2003 워크스패이스 (작업공간) 파일을 .ZIP 파일로 압축해서 제공하고 있습니다. 받아서 해보세요!

About the Author
저자에 관해

This is my second submission to CodeProject. The first demonstrated how to use Direct3D 8 to model the Munsell color solid so that you could then fly through this color cube as in a video game. I also have a web site where I offer a complete introduction to programming, including assembly language programming. My home page is:

이 문서는 코드프로젝트에 보내는 2번쨰 글입니다. 처음 보여준것은 Direct 3D 8을 이용해서 어떻게  Munsell 불투명 색을 모델해서 비디오 게임 내에 이 색을 구동(날리게)할수 있을지 보여준 내용이였습니다. 저는 또한 어셈블리 언어를 포함안 전체적인 프로그래밍의 소개를 담고 있는 웹사이트를 제공하고 있습니다. 저의 홈페이지는

http://www.computersciencelab.com/

John Kopplin

존 코핀

Posted by 카켈

출처 주소
http://www.sockaddr.com/ExampleSourceCode.html

바로 받기
- 원본 코드



- 새로 친 코드


변경 내용 - 약간의 한글화 / CheckMail.h 헤더 부분 내장 (내용이 작아서 넣었음)

수행 내용
-  TCP/IP - POP3 서버에 접속 및 통신하는 내용입니다.
- 수행화면

사용자 삽입 이미지

작업 로그

수행 방법
- 바로 수행되게 Link 부분을 수정했습니다.
#pragma comment (lib,"wsock32.lib")                        // wsock32.lib 를 링크하기
#pragma comment( linker, "/subsystem:windows" )    // WinMain 을 불러 쓰기. int main 을 쓰지                                                                                 않습니다.

- Resouce 부분은 직접 만들어서 추가했습니다. 처음이라 그런지 X 버튼으로 종료가 되지 않습니다.
- 프로젝트에 Resource 을 추가하시고 같이 생성되는 recource.h 파일을 include 하시면 됩니다.
- 워크스페이스(workspace)에 넣어 두시면 많이 편합니다(엉뚱하게 있어도 오류가 나는 경우가 있음) 추가하는 법은 아래 파일 트리에 Add Files in Project 를 누르시던지 Project 메뉴에 Add to Project -> Files 를 누르시면 됩니다.

사용자 삽입 이미지

추가된 리소스 트리와 파일입니다. MFC 를 쓰지 않고, WinAPI 를 썼습니다.

사용자 삽입 이미지

리소스 구조 입니다. 단순합니다;

- password 부분은 보이진 않지만 암호화 되어 전송되지 않기 때문에 외부 유출이 가능하므로 조심하시기 바랍니다.
- POP3 서버를 사용할수 있는 계정으로 접근 합니다.
- 만들어진 코드에서 문제가 시스템 명령 상으로 닫혀지지 않습니다. 아시는 분 댓글 부탁 드립니다. 해킹은 아닌데; 이상하게 X 버튼이 먹히지 않습니다. 그냥 닫기 컨트롤을 누르면 닫힙니다^^;

코드 보기(좀 깁니다.)
Posted by 카켈
출처 주소
http://www.sockaddr.com/ExampleSourceCode.html

바로 받기
- 원본 코드



- 새로 친 코드


변경 내용 - 약간의 한글화 / 0.5% 수정

수행 내용
-  TCP/IP 와 HTTP 사용하여 해당 서버에 접속 해당 파일을 받아 오는 내용(1024Bytes)입니다.
- 수행화면
코드 보기
Posted by 카켈
출처 주소
http://www.sockaddr.com/ExampleSourceCode.html

바로 받기
- 원본 코드


- 새로 친 코드


변경 내용 - 약간의 한글화

수행 내용
-  WinSock의 gethostbyX() 함수로 호스트 이름이나 IP 주소로   hostent 구조체 를 받습니다.
- 수행화면
사용자 삽입 이미지

코드 보기
Posted by 카켈
출처 주소
http://www.sockaddr.com/ExampleSourceCode.html

바로 받기
- 원본 코드


- 새로 친 코드


변경 내용 - 약간의 한글화

코드 보기
- 클라이언트 (winsock_3_1)

- 서버(winsock_3_2)

출력 화면
사용자 삽입 이미지



Posted by 카켈
출처 주소
http://www.sockaddr.com/ExampleSourceCode.html

바로 받기
원본



새로친거



수정 내용
1. 주석/부분 한글화

수행코드
- 클라이언트(winsock_2_1)

- 서버(winsock_2_2)


실행화면
사용자 삽입 이미지

서버 수행시 WinXP SP2 에서 방화벽 경고가 나오면 1차적으로 성공했습니다.

사용자 삽입 이미지

원본 코드와도 완벽하게 맞아 들어갑니다.

사용자 삽입 이미지

물론 새로 만든 코드와도 맞아 들어가죠

사용자 삽입 이미지

포트가 열려 있는걸 보실수 있습니다.
Posted by 카켈
받기
- 변형하지 않은 원본


- Visual Studio 6.0 에서 돌아갈수 있게 약간 손본 판


- 직접 한글화 시켜서 새로 친 판


원본 주소
http://www.sockaddr.com/ExampleSourceCode.html

코드 내용



원판과 변경점
1. 오류코드 및 부분 한글화
2. include 부분 변경

주의점
ws2_32.lib 를 프로젝트 메뉴에서 포함 시키시고 두 C 파일 모두 프로젝트 내에 있어야지 컴파일이 됩니다.

사용자 삽입 이미지




















 
프로젝트에 Link 탭에서 ws2_32.lib 를 꼭 확인해서 넣습니다.(이후 생략)


사용자 삽입 이미지


프로젝트 내에 있지 않으면 컴파일시 오류가 납니다.

출력화면
사용자 삽입 이미지

Posted by 카켈
컴퓨터 통신 연습 중입니다. 이거 작동이 되지 않네요. 리눅스용 PPT 내용을 제가 잘못보고 옮긴거 같습니다. 컴파일은 됩니다.

바로 받기



소스

클라이언트 쪽

서버 쪽


수행 결과
서버에서는 윈도우즈에서 방화벽 경고도 안뜨고 계속 무한 루프를 돌며 클라이언트에서는 문제 없다는 메시지만 보내고 끝을 냅니다.
사용자 삽입 이미지


Posted by 카켈
숙제로 하고 있는데 아직 잘 안됩니다...

코드 받기



원본 주소
http://www.exegesis.uklinux.net/gandalf/winsock/winsock1.htm

원본 코드

한글로 번역한 코드


컴파일시 주의사항

프로젝트 옵션에서 Link 탭, Object/library modules 에 ws2_32.lib 를 추가(Lib 링크 오류)하시고 Project Options 에 보시면 ws2_32.lib /nologo /subsystem:console 로 맞춰 주시야지 Link 오류(WinMain16 링크 오류)가 생기지 않습니다.

사용자 삽입 이미지




















 
실행 결과 : 원본 코드 / 친 코드 모드 작동되지 않았습니다. Finger 서버 자체에 접속이 안되었습니다. 컴파일 환경이 달라서 그런거 같습니다.

사용자 삽입 이미지
사용자 삽입 이미지





Posted by 카켈



  C 에 이어서 C++ 입니다. 따로 공부 하시는게 의외로 상성 효과로 많은 도움이 되실겁니다. 처음부터 약간 객체지향적인 코딩 부분이 있기 때문에 생각을 다른 시각에서 보셔야 합니다. C 에서도 그랬지만 이렇게 작동한다는 객체, 도구로 보셔야 합니다. C 문법을 지녔지만 하는 기능과 역활은 전혀 새로울수 있습니다.

  저작권 공지 : 이 강의 노트의 저작권은  고려대학교 신호처리연구실에 있습니다. 그러니 원저작자의 허락없이 무단으로 짜집기하시거나 자기가 만들었다고 살짝 바꿔서 우기시면 안 됩니다;
http://signal.korea.ac.kr/~ychlee 에서 받았습니다.
Posted by 카켈


 
C 와 C++ 에 대해 마무리 하고 한동안 다른데에 매진 해야겠습니다. 이번 학기가 너무 힘들군요. C 와 C++ 은 공돌이에게 있어서 기본적인 도구가 될 경우가 많습니다. 저역시 그 중에 하나인데요. 이번에 확실히 기초는 다져진거 같습니다. 풀다 보니 많이 부족하더군요.

  저처럼 막힐때 어떻게 할까 아니면 처음부터 시작 하고 싶은데 어떻게 할까 싶으신 분들을 위해 강의 노트를 올렸습니다. 전 이때 포트란이라는 전설의 언어를 배웠기 때문에 관계 없는 노트지만 정말 좋은 내용입니다. 받아서 실습해보시고 많은 경험을 얻으시기 바랍니다.

  저작권 공지 : 고려대학교 신호처리연구실 에서 나온 강의 자료 입니다. 내용이 귀중하기 떄문에 요청으로 지워질수 있습니다. 원 저작자의 허락없이 변경하는 건 저도 책임 못집니다; http://signal.korea.ac.kr/~ychlee
Posted by 카켈



  문제가 새로운게 많았습니다. 조금 난해한 부분도 있었지만 슬기롭게 해낸거 같습니다. 2004년 까지만 해도 크게 차이가 없었는데 2005년 부터는 확실히 무언가 달라지고 있다는 것을 느꼈습니다. 비록 이게 마지막 시험이라 아쉽지만 이번학기에 나올 객체 시험 문제에 대해 기대하고 있습니다. 방학때 다시 C++ 언어를 새로 공부 할수 있을거 같군요. 클래스를 상속까지 진보 시켰으며 포인터 연산자에 대해 새롭게 알았습니다.
 
  저작권 공지
  문제/해답 : http://signal.korea.ac.kr/~ychlee
  코드 : http://cakel.tistory.com
  교육용을 목적으로 자유롭게 쓰실수 있습니다.
Posted by 카켈




목적
  - 문자열에서 사전정렬을 이해한다.

문제

사용자 삽입 이미지

코드

#include <iostream>
#include <cstring>
#include <ctype.h>

using namespace std;

int main(void)
{
 char sIn[4][255] = {0};
 char sSwap[255] = {0};
 char cBuf = ' ';
 long lSwap = 0;
 long lValue[4] = {0};
 int i, j;
 i = j = 0;

 while(i < 4)
 {
  cout << i + 1 << "번째 문자열 입력 : ";
  cin.getline(sIn[i],255);

  while(cBuf != 0)
  {
   cBuf = sIn[i][j];

   if(cBuf != ' ')
   {
   
    if(islower(cBuf))
    {
     lValue[i] = (int)cBuf - 60;
     break;

    }

    if(isupper(cBuf))
    {
     lValue[i] = (int)cBuf - 55;
     break;

    }

    if(isdigit(cBuf))
    {
     lValue[i] = (int)cBuf - 48;
     break;
    }

   }
 
   j++;
  }
 
  cBuf = ' ';

  j = 0;
  i++;

 }

 i = 0;
 j = 0;
 
 while(i < 4)
 {
  j = i;
  while(j < 4)
  {
   if(lValue[i] < lValue[j])
   {
    lSwap = lValue[j];
    lValue[j] = lValue[i];
    lValue[i] = lSwap;

    strcpy(sSwap, sIn[i]);
    strcpy(sIn[i], sIn[j]);
    strcpy(sIn[j], sSwap);

   }

   j++;
 
  }

  i++;
 }

 i = 0;
 cout << endl << "크기순으로 정렬된 문자열 : " << endl;
 while(i<4)
 {
  cout << sIn[i] << endl;
  i++;

 }
 

 return 0;
}

해설
  - 코드가 약간 비효율적입니다.
  - 문제를 제대로 이해하시면 상당히 쉽습니다. 사전정렬을 하되 소문자 > 대문자 > 숫자 그룹별로 숫자가 크며, 사전순으로 알파벳 순서가지날수록 값이 커집니다. 이 숫자를 각 문자열의 첫째 글자만  맞춰서 문자열을 내림차순으로 맞춥니다.

  - 문제를 이해하지 못해서 값을 더하면 엉뚱한 결과가 나옵니다.
  - 예제에서 'V' > 'M' > 'I' > 'H' 이라서 순서가 실행결과 처럼 나왔습니다.
  - 첫째 글자가 검출되면 계산후 값을 저장하고 break로 탈출합니다.

참고
  - 해답에는 vector 알고리듬을 쓰던데 크게 차이는 없습니다.
  - 유사 문제 검색

Posted by 카켈



목적
  - 클래스의 상속에 대해서 이해하고 활용한다.

문제

사용자 삽입 이미지

코드







해설
  - 이전 문제의 업그레이드 판으로 상속에 대해서 이해를 하셔야지 풀수 있는 문제입니다.
  - 상속은 ThreeD -> Cubic 으로 되는데 protected 된 자료형과 public 으로 된 자료형을 상속 받습니다.
  - 상속 받은 클래스의 객체가 초기화 될때 부모 클래스에서 자동으로 생성자를 실행하기 위해 상속 클래스에 : [부모 클래스의 생성자 함수] 를 써줍니다. 그러면 리턴되어 자식 클래스에서도 똑같이 적용이 됩니다.

  - 셋다 0이 아닐때 수행되며 셋중 하나가 0이 되면 종료되는 판정문이 있습니다.
  - 이전에 생성된 객체는 그다지 중요하지 않기 때문에 1차원 포인터로 객체를 마구 찍을수 있게 했습니다.

참고
  - 상속은 처음 나온 문제 입니다.
  - 예제 코드 보고 이해하기엔 참고 해야할 사항들이 많습니다. 처음 보시는 분들은 클래스의 다른 정보들도 같이 이해하시기 바랍니다.
  - http://winapi.co.kr/clec/cpp3/29-1-2.htm : 상속의 예
  - 이전 문제
Posted by 카켈



목적
  - 배열을 동적으로 생성시키고 클래스에다가 적용시킨다.
  - inline 함수에 대해 이해한다.
  - 생성자를 활용한다.
  - 이중 포인터에 대한 이해를 한다.

문제

사용자 삽입 이미지

코드


해설
  - 복사 생성사는 매너(?) 상 넣었습니다. 포인터 맴버 변수를 가진 클래스에서는 언제 쓰일지 모르기 때문에 써주시는게 예상치 못한 오류를 막는데 도움이 될수 있습니다.

  - inline 함수들만 정의 되어 있어서 굳이 Employee.cpp 파일을 작성할 필요가 없습니다. Employee.h 에서 다 정의 했습니다.

  - 코드에 해설이 주석으로 직접적으로 쓰여져 있습니다.

  - 이번엔 이중 포인터라는 중요한 개념을 익혔습니다. 일차포인터 배열은 가리키는 주소가 스택에 있기 때문에 그 크기가 변할수 없지만 이중포인터로 이차원으로 선언했을때에는 가리키는 주소가 힙에 저장되기 때문에 배열의 크기가 코드상에서 동적으로 지정이 가능합니다. 중요한 조언을 주신 선배님께 감사드립니다.

  - cin 으로 엔터키를 입력하면 엔터 값이 버퍼에 남아 있기 때문에 cin.getline 을 해버리면 넘어가버리는 문제가 있었습니다. cin.ignore(1,'\n') 버퍼에서 값을 가져올때 첫번째 '\n' (줄내림) 까지 값을 무시하고 받는다고 명령을 넣고 다음 키 입력 getline 함수를 이용하여 저장을 할수 있습니다.

  - 동적 배열을 구현할수 있지만 사용자 정의된 클래스에서는 약간 복잡해집니다. 따라서 MFC 나 STL 에서 동적 배열을 구현하는 자료형을 제공합니다.

  - inline 함수는 일반함수의 개념보다 매크로의 개념이 강합니다. 해당 함수가 호출되었을때 inline으로 저장이된 구문이 함수를 호출한 코드에서 복사/붙여넣기 처럼 그 자리에서 실행을 합니다. 원래 함수는 호출 -> 현재 상태 저장 -> 복귀값 저장 -> 처리 -> 이전 상태 복귀 -> 리턴값과 함께 이전 상태로 복귀 이런 식으로 가는데, inline 함수는 코드 호출 -> 치환 -> 수행 -> 리턴값 이런식으로 간단하게 수행됩니다. 수행 시간이 짧아지는 장점과 동시에 코드가 그떄 그때 붙어버리는 바람에 커져버리는 단점이 있습니다. 구조체나 클래스 처럼 마지막에 } 하면서 세미콜론을 잊지 마시기 바랍니다. 정의의 마지막에는 꼭 필요합니다.


참고
  - http://www.winapi.co.kr/clec/cpp1/10-4-1.htm 
: 이중 포인터
  - http://www.winapi.co.kr/clec/cpp2/19-1-2.htm
 : 동적 배열
  - http://www.borlandforum.com/impboard/impboard.dll?action=read&db=component&no=441

    : 구조체를 동적 배열로 관리할 수 있는 클래스 - 동적 배열 응용 예
  - 유사 문제 검색
Posted by 카켈




목적
  - 문자열을 동적 할당으로 입력받아 자소를 비교 한다.

문제

사용자 삽입 이미지
코드

#include <iostream>
#include <cstring>
#include <ctype.h>

using namespace std;

int main(void)

{
 char sIn[255] = {0};
 int i, iAlpha, iSpace, iSpecial, iMoum;
 i = iAlpha = iSpace = iSpecial = iMoum = 0;
 char cBuf;
 char* sString = NULL;

 cout << "문자열 입력 : ";
 cin.getline(sIn,255);

 sString = new char[strlen(sIn) + 1];
 strcpy(sString, sIn);

 while(sString[i] != 0)
 {
  if(isalpha(sString[i]))
   iAlpha++;

  if(isspace(sString[i]))
   iSpace++;

  if(ispunct(sString[i]))
   iSpecial++;
 
  cBuf = tolower(sString[i]);

  if(cBuf == 'a' || cBuf == 'e' || cBuf == 'i' || cBuf == 'o'
   || cBuf == 'u')
   iMoum++;

  i++;

 }

 cout << "문자열의 길이 : " << strlen(sString) << endl;
 cout << "알파벳문자 개수 : " << iAlpha << endl;
 cout << "공백문자   개수 : " << iSpace << endl;
 cout << "특수문자   개수 : " << iSpecial << endl;
 cout << "모음의     개수 : " << iMoum << endl;

 return 0;
}



해설
  - 문자별료 배열 포인터를 지정 하여 자소별로 접근할수 있게 했습니다.
  - A E I O U / a e i o u 는 소문자로 일괄적으로 비교하여 비교하게 했습니다.

참고
  - 유사 문제 검색

Posted by 카켈




목적
  - 문자열을 받아 판단하여 조건문을 실행하게 한다.

문제
사용자 삽입 이미지

코드

#include <iostream>
#include <cstring>

using namespace std;

int main(void)
{
 int i, iCurriculum, iScr;
 float fTotal, fScrTotal;
 char sGrade[2] = {0};
 i = 1;
 fTotal = fScrTotal = iCurriculum = 0.0;

 cout << "수강과목 수 : ";
 cin >> iCurriculum;

 while(i<= iCurriculum)
 {
  cout << i << "번째 과목 학점과 등급 : ";
  cin >> iScr >> sGrade;

  if(!strcmp(sGrade,"A+"))
   fTotal += 4.5 * iScr;

  if(!strcmp(sGrade,"A"))
   fTotal += 4.0 * iScr;

  if(!strcmp(sGrade,"B+"))
   fTotal += 3.5 * iScr;

  if(!strcmp(sGrade,"B"))
   fTotal += 3.0 * iScr;

  if(!strcmp(sGrade,"C+"))
   fTotal += 2.5 * iScr;

  if(!strcmp(sGrade,"C"))
   fTotal += 2.0 * iScr;

  if(!strcmp(sGrade,"D+"))
   fTotal += 1.5 * iScr;

  if(!strcmp(sGrade,"D"))
   fTotal += 1.0 * iScr;

  fScrTotal += iScr;
  i++;

 }

 cout << "이번학기 학점 : " << fTotal / fScrTotal << endl;

 return 0;
}

해설
  - F 학점은 fTotal 더하지 않기 때문에 판정을 하지 않아도 문제 없습니다.
  - 다중 if 문으로 else 없이 써도 한 루프에 하나의 if 만 걸리기 때문에 문제 없이 작동할수 있습니다.
  - float 형으로 선언하여 소숫점 계산을 할수 있습니다.
  - 문자형 이나 기타 단순 비교용 == 연산자로 sGrade == "A+" 이렇게 될거 같지만 문자열에 대한 비교는 이 연산자로는 되지 않습니다. 대신 strcmp 함수를 쓰면 가능합니다.

참고
  -유사 문제 검색

Posted by 카켈





목적
  - 벡터를 이해하고 수학 공식에 적용 시켜 문제를 해결한다.

문제
사용자 삽입 이미지

  - (수정) 실행결과는 아래와 같습니다.
사용자 삽입 이미지


코드

#include <iostream>
#include <vector>
#include <cmath>

using namespace std;

int main(void)
{
 vector<double> stlVector;
 vector<double>::iterator pVec;
 double dIn, dTotal, dAvg, dVar;
 dIn = dTotal = dAvg = dVar = 0.0;

 cout << "실수 값 입력(입력종료:0) : ";
 cin >> dIn;

 while( dIn != 0 )
 {
  stlVector.push_back(dIn);
  cin >> dIn;
 }

 for(pVec = stlVector.begin(); pVec != stlVector.end(); pVec++)
  dTotal += *pVec;

 dAvg = dTotal / (int)stlVector.size();
 dTotal = 0;

 for(pVec = stlVector.begin(); pVec != stlVector.end(); pVec++)
  dTotal += (*pVec - dAvg) * (*pVec - dAvg);
// pow((*pVec - dAvg), 2) 를 써도 됩니다.

 dVar = sqrt(dTotal / (double)stlVector.size());

 cout << "평    균 : " << dAvg << endl;
 cout << "표준편차 : " << dVar << endl;

 return 0;
}



해설
  - 표준 편차를 구하는 식은  아래와 같습니다.

사용자 삽입 이미지

  - pVec 는 vector STL 자료형을 가리키는 포인터 역할을 합니다.
  - 제가 잘못한거 같아서 여러번 검산을 거친 끝에 저 값이 맞는 것을 확인 했습니다.

   + 1차 확인 : Microsoft Excel 2003

  - MS-Excel 에서 표준편차를 구하는 함수는 여러가지가 있습니다. stdev stdevp ... 중에서 이번 공식에 맞는 식은 stdevp 이였습니다.

사용자 삽입 이미지

위 계산 값을 저장하여 첨부에 넣었습니다.



  - 각 함수의 차이

사용자 삽입 이미지

STDEVP 함수 : 원하는 식입니다.

사용자 삽입 이미지

STDEV 함수 : 항의 갯수가 하나 줄어듭니다. (표본차이)


 - STDEV 함수는 인수들을 모집단의 표본으로 간주합니다. 데이터 집합이 모집단 전체를 나타내면 STDEVP 함수를 사용하여 표준 편차를 계산해야 합니다. (Microsoft EXCEL 도움말 인용)

   + 2차 확인 : Matlab 7.1

사용자 삽입 이미지


std 함수를 쓰면, Microsoft Excel 과 같은 결과를 얻습니다.


참고
  - 결과가 이상할 경우 3자(엑셀은 편리합니다.) 툴을 써서 검증하면 편리할수 있습니다.
  - 유사 문제 검색
Posted by 카켈




목적
   - 동적 할당을 이해하고 문자열에 적용 시킨다.

문제
사용자 삽입 이미지

코드

#include <iostream>
#include <cstring>

using namespace std;

int main(void)
{
 char sIn[255] = {0};
 char *sString = NULL;
 int i = 0;

 cout << "문자열 입력 : ";
 cin.getline(sIn,255);

 sString = new char[strlen(sIn) + 1];
 strcpy(sString, sIn);

 cout << "공백제외한 역순 출력 :";

 for(i = strlen(sString); i >= 0; i--)
 {
  if (sString[i - 1] != ' ')
   cout << sString[i];
 }
 
 cout << endl;

 sString = NULL;
 delete sString;

 return 0;
}

해설
  - sString 은 시작시 단일 문자를 저장하는 주소를 담는 4바이트 문자형 포인터입니다. sIn 에 값이 들어 오면 길이를 확인하여 동적으로 sString 를 늘려 줍니다. 프로그램 상에서 동적으로 늘렸기 떄문에 마칠때 delete 로 삭제를 해줍니다.

참고
  - 유사 문제 검색
Posted by 카켈





목적
  - 시간의 단위를 이해하고 C++ 에 적용 시킨다.

문제
사용자 삽입 이미지

코드

#include <iostream>

using namespace std;

int main(void)
{
 int iHour_A, iMin_A, iSec_A, iAddSec, iHour_B, iMin_B, iSec_B;
 iHour_A = iMin_A = iSec_A = iAddSec = iHour_B = iMin_B = iSec_B = 0;

 cout << "현재 시간을 입력 : ";
 cin >> iHour_A >> iMin_A >> iSec_A;

 cout << "추가할 시간(초) : ";
 cin >> iAddSec;

 iHour_B = (iHour_A * 3600 + iMin_A * 60 + iSec_A + iAddSec) / 3600;
 iMin_B = (iHour_A * 3600 + iMin_A * 60 + iSec_A + iAddSec) % 3600 / 60;
 iSec_B = (iHour_A * 3600 + iMin_A * 60 + iSec_A + iAddSec) % 60;

 cout << iHour_A << "시 " << iMin_A << "분 " << iSec_A << "초 부터 "
  << iAddSec <<"초 후 시간 : "
  << iHour_B << "시 " << iMin_B << "분 " << iSec_B << "초" << endl;

 return 0;
}

해설
  - 시간은 전체 초를 3600초로 나누고 분은 전체초를 3600 한 나머지를 60으로 나누고 초는 전체 초를 60으로 나눈 나머지를 구하면 됩니다. 즉 그 단위에 맞는 숫자로 나누면 그 단위에 맞는 숫자가 나오며 그이하의 단위의 값을 구할려면 바로 상위 단위의 동등 숫자를 나눈 나머지를 그 아래 동등 숫자로 나눈 몫을 대입하면 나옵니다.

참고
  - 유사 문제 검색

Posted by 카켈




목적
  - 문자열을 다룰줄 안다.

문제

사용자 삽입 이미지

코드

#include <iostream>

using namespace std;

int main(void)
{
 char sStrA[255] = {0};
 char sStrB[255] = {0};

 cout << "첫번째 문자열 : ";
 cin.getline(sStrA,255);

 cout << "두번째 문자열 : ";
 cin.getline(sStrB,255);
 
 cout << "병합된 문자열 : ";
 if (strlen(sStrA) <= strlen(sStrB))
  cout << sStrA << sStrB << endl;

 else
  cout << sStrB << sStrA << endl;

 return 0;
}



해설
  - 자료의 효율성은 없지만 최대한 편리하게 작성 했습니다.
  - strlen 함수로 문자의 길이를 비교 하고 첫번째 문자열이 두번째 문자열 작거나 같다면 그대로 출력하고 그렇지 않고 두번째 문자열이 더 짧다면 바꿔서 출력하는 알고리듬을 가지고 있습니다.

참고
  - 유사 문제 검색

Posted by 카켈




목적
  - 동적 할당에 대해 이해하고 입력을 받아서 자료를 추가 및 처리 할줄 안다.

문제
사용자 삽입 이미지

코드

#include <iostream>
#include <cstring>
#include <vector>

using namespace std;

int main(void)
{
 vector<double> stlVector;
 int i = 0;
 char *sName, *sId;
 char sIn[255] = {0};
 double dIn, dTotal;
 dIn = dTotal = 0.0;
 sName = sId = NULL;

 cout << "학번 : ";
 cin.getline(sIn,255);
 sId = new char[strlen(sIn) + 1];
 strcpy(sId, sIn);

 cout << "이름 : ";
 cin.getline(sIn,255);
 sName = new char[strlen(sIn) + 1];
 strcpy(sName, sIn);

 cout << "네 과목의 성적 : ";
 for(i = 0; i <= 3; i++)
 {
  cin >> dIn;
  stlVector.push_back(dIn);
  dTotal += stlVector[i];

 }
 
 cout << sId << ' ' << sName << " 의 평균 점수는 " << dTotal / i << " 입니다." << endl;
 delete sName, sId;

 return 0;
}

해설
   - vector STL 자료형은 이전 문제에서 자세히 다루었습니다. 참고 하시기 바랍니다.
  - 나머지 부분은 크게 어렵지 않습니다.
  - 문자열 포인터를 생성후 동적으로 입력 받은 글자의 길이만큼 새로 생성하여 추가하는 방식을 썼습니다. 생성 및 삭제에 new / delete 명령어가 쓰입니다.

참고
  - vector STL 다루었던 문제

Posted by 카켈



  2005년 중간고사 모음입니다. 문제의 갯수는 줄었지만 새로운 문제들과 vector STL 로 인해 체감 난이도는 조금 상승했습니다. 제가 많이 부족하다는걸 생각나게 만든 문제들이였습니다. 덕분에 이전 연도 문제들과 달리 많은 고민을 했습니다. 풀었으니 뿌듯하군요.

  저작권 공지
  문제/해답 : http://signal.korea.ac.kr/~ychlee
  코드 : http://cakel.tistory.com
  교육용을 목적으로 자유롭게 쓰실수 있습니다.
Posted by 카켈