/*
1. 스레드는 각자의 스택을 가진다.
2. 멀티스레드 환경에서 지역변수는 안전하다. -어떤것을 얼마나 사용하던 상관이 없다.
3. 멀티 스레드 환경에서 전역(static 지역) 변수는 불안하다.
반드시 동기화 해야한다.
CriticalSection, Mutex, Semaphore, Event 등의 기범을 사용해야한다.
- CriticalSection 구역에는 반드시 하나의 스레드만 존재한다. - 상호 배제
구역을 들어가기 전에 Enter() 나가면 Leave()먼저 온것이 들어가면 나중에 온것은 먼저 온것이 나갈때까지 기다려야한다.- 독점
하지만 먼조 온 스레드가 나가더라도 다시 실행하려고 하면 두 스레드는 다시 경쟁상태에 놓이게 된다.
*/
/*
#include<windows.h>
#include<stdio.h>
void Delay(){ for(int i = 0 ; i < 20000000 ; i++); }//시간지연
//BOOL bWait = TRUE;
CRITICAL_SECTION cs;
DWORD WINAPI foo(void *p)
{
char * name = (char *)p;
static int x = 0;
for (int i = 0 ; i < 20 ; i++)
{
EnterCriticalSection(&cs); // CriticalSection에 들어간다.
//========================================================
x = 100; Delay();
x =x + 1; Delay();
printf("%s : %d\n",name,x); Delay();
//========================================================
LeaveCriticalSection(&cs); // CriticalSection에서 나온다.
}
printf("Finish...%s\n",name);
// bWait = FALSE;
return 0;
}
//메인은 스레드 하나를 만들어 놓고 죽는다. 즉 주 스레드가 죽는것이다.
//이 경우 생성된 스레드도 같이 죽는다. 이유는 프로세스가 죽기 때문.
void main()
{
InitializeCriticalSection(&cs); // 아래에 반드시 DeleteCriticalSection()이 존재해야 한다.
HANDLE h1 = CreateThread(0,0,foo,"A",0,0);
HANDLE h2 = CreateThread(0,0,foo,"\tB",0,0);
//스레드는 죽을때 시그널 된다.
//WaitForSingleObject(h1,INFINITE); //하나만 대기한다.
HANDLE h[2] = {h1,h2};
//64개까지 가능하다.
WaitForMultipleObjects(2,h,TRUE,INFINITE); //여러개를 대기한다. 여기서는 2개가 모두 시그널 될때까지 대기한다.
//while(bWait);//절대 사용하면 안되는 최악의 방법 하는일 없이 CPU를 사용하게 된다.
CloseHandle(h1);//사실상 여기서 종료를 안해도 프로그램이 종료한다.
CloseHandle(h2);
// ExitThread(0); // 현재 프로세스만 종료 -> 자신만 종료 즉 주 스레드만 종료
//return ; // 프로세스의 종료
DeleteCriticalSection(&cs);
}
*/
/*
CriticalSection관련함수 4개
CRITICAL_SECTION 변수명; <~ 변수 설정
EnterCriticalSection();
LeaveCriticalSection();
InitializeEnterCriticalSection();
DeleteCriticalSection();
*/
/*#include<windows.h>
#include<iostream>
#include<list>
using namespace std;
list<int> st; //전역 객체(모든 멤버가 static에 있으므로 모든 스레드의 의해 공유된다 접근시 동기화가 필요하다.
#define COUNT 10
DWORD WINAPI foo(void *p)
{
for(int i = 1 ; i < 10 ; i++)
{
for(int j = 1 ; j < COUNT; j++)
{
st.push_back(10); //링크드 리스트 접근
}
printf("foo : %d\n",i);
}
return 0;
}
DWORD WINAPI goo(void *p)
{
for(int i = 1 ; i < 10 ; i++)
{
for(int j = 1 ; j < COUNT; j++)
{
st.push_back(10); //링크드 리스트 접근
}
printf("goo : %d\n",i);
}
return 0;
}
void main()
{
HANDLE h[2];
h[0] = CreateThread(0,0,foo,0,0,0);
h[1] = CreateThread(0,0,goo,0,0,0);
WaitForMultipleObjects(2,h,TRUE,INFINITE);
CloseHandle(h[0]);
CloseHandle(h[1]);
}*/
/*
#include<windows.h>
#include<iostream>
#include<list>
using namespace std;
list<int> st; //전역 객체(모든 멤버가 static에 있으므로 모든 스레드의 의해 공유된다 접근시 동기화가 필요하다.
#define COUNT 50000
//리스트를 통기화 하기 위한 크리티걸 섹션
CRITICAL_SECTION cs;
CRITICAL_SECTION cs2; //printf()를 위한 동기화
DWORD WINAPI foo(void *p)
{
for(int i = 1 ; i < 10 ; i++)
{
for(int j = 1 ; j < COUNT; j++)
{
EnterCriticalSection(&cs);
st.push_back(10); //링크드 리스트 접근
LeaveCriticalSection(&cs);
}
EnterCriticalSection(&cs2);
printf("\tfoo : %d\n",i);
LeaveCriticalSection(&cs2);
}
return 0;
}
DWORD WINAPI goo(void *p)
{
for(int i = 1 ; i < 10 ; i++)
{
for(int j = 1 ; j < COUNT; j++)
{
EnterCriticalSection(&cs);
st.push_back(10); //링크드 리스트 접근
LeaveCriticalSection(&cs);
}
EnterCriticalSection(&cs2);
printf("goo : %d\n",i);
LeaveCriticalSection(&cs2);
}
return 0;
}
void main()
{
InitializeCriticalSection(&cs);
InitializeCriticalSection(&cs2);
HANDLE h[2];
h[0] = CreateThread(0,0,foo,0,0,0);
h[1] = CreateThread(0,0,goo,0,0,0);
WaitForMultipleObjects(2,h,TRUE,INFINITE);
CloseHandle(h[0]);
CloseHandle(h[1]);
DeleteCriticalSection(&cs);
DeleteCriticalSection(&cs2);
}*/
// Thread - Safe 클래스 : 싱글 스래드나 멀티 스레드 환경에서 아무 문제 없이
//사용 할 수 있다.(즉 자신이 스스로를 동기화 하고 있다.)
//stl의 list에서 파생된 클래스를 만든다.
/*
Serialization : 동시에 수행중이던 스레드가 어떤 지점을 직렬로 차례로 통과 하게 되는 것을 일컫는 용어
주의 : MFC에서는 다른 용도로 이 용어를 사용하기도 한다.
*/
/*
#include<windows.h>
#include<iostream>
#include<list>
using namespace std;
CRITICAL_SECTION cs;
template<typename T>class Safe_List : public list<T>
{
CRITICAL_SECTION m_cs;
public:
Safe_List()
{
InitializeCriticalSection(&m_cs);
}
~Safe_List()
{
DeleteCriticalSection(&m_cs);
}
//부모함수 오버라이드
void push_back(const T& x)
{
EnterCriticalSection(&m_cs);
list<T>::push_back(x); // 부모의 원래 기능 사용
LeaveCriticalSection(&m_cs);
}
};
Safe_List<int> st; //전역 객체(모든 멤버가 static에 있으므로 모든 스레드의 의해 공유된다 접근시 동기화가 필요하다.
#define COUNT 10
DWORD WINAPI foo(void *p)
{
for(int i = 1 ; i < 100000 ; i++)
{
for(int j = 1 ; j < COUNT; j++)
{
st.push_back(10); //링크드 리스트 접근
}
EnterCriticalSection(&cs);
printf("foo : %d\n",i);
LeaveCriticalSection(&cs);
}
return 0;
}
DWORD WINAPI goo(void *p)
{
for(int i = 1 ; i < 100000 ; i++)
{
for(int j = 1 ; j < COUNT; j++)
{
st.push_back(10); //링크드 리스트 접근
}
EnterCriticalSection(&cs);
printf("\tgoo : %d\n",i);
LeaveCriticalSection(&cs);
}
return 0;
}
void main()
{
InitializeCriticalSection(&cs);
HANDLE h[2];
h[0] = CreateThread(0,0,foo,0,0,0);
h[1] = CreateThread(0,0,goo,0,0,0);
WaitForMultipleObjects(2,h,TRUE,INFINITE);
CloseHandle(h[0]);
CloseHandle(h[1]);
DeleteCriticalSection(&cs);
}*/
/*
1. 스택은 스레드별로 하나씩 갖는다.
2. 스태틱은 프로세스가 1개 갖는다.
3. 쓰래드별로 하나의 스태틱을 갖고 싶다 - 생긴게 -> ThreadLocalStrorage
*//*
#include<windows.h>
#include<iostream>
#include<list>
using namespace std;
void goo(char * name)
{
//TLS 공가넹 변수를 생성한다.
__declspec(thread) static int c = 0;
++c;
printf("%s : %d\n",name,c ); // 함수가 몇번 호출되었는지 알고 싶다.
}
DWORD WINAPI foo(void *p)
{
char * name = (char *)p;
goo(name);
goo(name);
goo(name);
return 0;
}
void main()
{
//InitializeCriticalSection(&cs);
HANDLE h[2];
h[0] = CreateThread(0,0,foo,"A",0,0);
h[1] = CreateThread(0,0,foo,"\tB",0,0);
WaitForMultipleObjects(2,h,TRUE,INFINITE);
CloseHandle(h[0]);
CloseHandle(h[1]);
//DeleteCriticalSection(&cs);
}*/
/*
정리 :
어떤 함수가 내부적으로 지역변수만 사용한다. -> 멀티 스레드에 안전한가?(안전)
어떤 함수가 내부적으로 static 변수를 사용한다. -> 싱글스레드는 문제 없다.
-> 멀티 스레드는 안전한가? (불안전)
1.VC++의경우 LIBC.lib에 C표준 함수가 들어있다. - 싱글 스레드용
2.LIBCMT.lib - 멀티스레드용 - 스레드를 사용하기 위해서는 초기화가 필요하다
- 대신해주는것 -> _beginthreadex()
*/
/*
#include<stdio.h>
#include<process.h> // _beginthreadex()를 사용하기 위해
#include<windows.h>
unsigned long __stdcall foo(void *p) // 결국 DWORD WINAPI foo(void *p)
{
printf("foo\n");
return 0;
}
void main()
{
unsigned long h=_beginthreadex(0,0,foo,0,0,0);
WaitForSingleObject((HANDLE)h,INFINITE);
CloseHandle((HANDLE)h);
}
*/
/*
프로그램을 만들때
C의 많은 함수를 사용한다.(strtok()같은 함수 ) _beginthreadex()를 사용해야 한다.
순수 Win32API를 만을 사용한다 -> CreateThread() 사용
순수 Win32API와 C의 일부 함수들 (스레드에 안전한) - > CreateThread()를 사용해도 되자만
확신이 없다면 _beginthreadex() 사용
MFC 사용 : AfxBegineThread() 라는 MFC전용 함수를 사용한다.
*/
#include<windows.h>
#include<stdio.h>
#include<conio.h>
void main()
{
HANDLE hMutex = CreateMutex(0, //보안속성
FALSE, //소유권여부
"m"); //뮤텍스 KO이름
printf("뮤텍스를 대기 합니다.\n");
WaitForSingleObject(hMutex,INFINITE);
printf("뮤텍스를 획득했습니다.\n");
getch();
ReleaseMutex(hMutex);
CloseHandle(hMutex);
}