190703-2
교육을 받으면서 노트필기 했던 내용을 날것 그대로 업로드합니다.
-
동기화 :
-
비동기화 : 시간 t에 독립적으로 두개 이상 작업이 수행 (중첩, 겹침)
크리티컬 색션 : 동기화 객체라고 부른다.
-
동기화 객체
-
InterLocked~
-
Critical Section : User Level
-
Mutex : Kernel Level
-
Semaphore : Kernel Level : ↑ 데이터 보호를 위해 만들어졌다.
-
Event : Kernel Level : 순서제어를 위해 만들어 졌다.
-
유저모드 동기화
-
크리티컬 섹션 : 메모리 접근 동기화
-
인터락 함수 : 메모리 접근 동기화
-
커널모드 동기화
-
뮤텍스 : 메모리 접근 동기화 (소유개념), 소유하면 비신호, 소유하고있지않으면 신호 상태가된다.
-
세마포어 : 메모리 접근 동기화 (소유개념이 없다.) 소유자가 아니더라도 Release를 할 수 있다. 자원안에 자원을 사용할 수 있는 개수를 정할 수 있다. 세마포어가 한개이더라도 한개가 자원을 동시에 갖도록할 수 있다. (자원의 개수가 남아있으면 신호상태), 카운팅 기능이 존재한다.
-
named 뮤텍스 : 프로세스간 동기화 - 이름을 가진다면 여러 프로세스가 공유 가능하다.
-
이벤트 : 시행순서 동기화
-
뮤텍스는 획득한 스레드가 직접 반환해주어야한다.
-
세마포어나 그 외 동기화 오브젝트는 다른 곳에서 대신 반환해줘도 된다.
● 데이터보호와 순서제어에 사용된다.
● 임계영역(Critical Section) 설정
for(int i = 0 ; i < 1000000 ; ++i)
{
EnterCriticalSection(&cs); //임계 영역(보호) 시작
val++;
LeaveCriticalSection(&cs); //임계 영역 끝
}
#include <stdio.h>
#include <windows.h>
CRITICAL_SECTION cs; //● os가 임계 영역을 관리하는데 사용된다.
int val=0;
DWORD __stdcall ThreadFunc(LPVOID pParam)
{
printf("ThreadFunc Param : %d 생성\n", GetCurrentThreadId());
for(int i = 0 ; i < 1000000 ; ++i)
{
EnterCriticalSection(&cs); //임계 영역(보호) 시작
val++;
LeaveCriticalSection(&cs); //임계 영역 끝
}
return 0;
}
void main()
{
DWORD threadID;
HANDLE hThread[2];
InitializeCriticalSection(&cs); // 생성 //● 초기화가 필요하다.
hThread[0] = CreateThread(0,0,ThreadFunc, (LPVOID)0, 0, &threadID);
hThread[1] = CreateThread(0,0,ThreadFunc, (LPVOID)0, 0, &threadID);
//쓰레드 종료 대기(모든 쓰레드의 신호상태를 기다림)
WaitForMultipleObjects(2, hThread, TRUE, INFINITE);
printf("val : %d\n", val);
DeleteCriticalSection(&cs); //제거 //● 사용 다 했을 경우 제거한다.
CloseHandle(hThread);
}
Sleep(0) : 블로킹 상태에 놓겠다는 뜻. 시간 대기는 안함
● 뮤텍스 : 소유라는 개념이 있다.
소유가 되면 비신호
소유 되있지 않으면 신호상태
#include <stdio.h>
#include <windows.h>
HANDLE hMutex; // ●커널오브젝트라 핸들을 가짐.
DWORD __stdcall ThreadFunc(LPVOID pParam)
{
int num = (int)pParam;
printf("ThreadFunc Param : %d 생성\n", num);
WaitForSingleObject(hMutex, INFINITE);// ● 신호상태를 대기 - 자기 자신(쓰레드)을 블로킹 상태로 만든다.
// ●호출시 신호상태였다면 해당 쓰레드가 소유하게된다.(비신호상태로 만든다)
//핸들로 뮤텍스를 준다면 기다리다가 소유하게된다. (ES사 실기시험)
for(int i = 0 ; i < 10 ; ++i)
{
printf("ThreadFunc[%d] : %d\n",num, i);
Sleep(200);
}
ReleaseMutex(hMutex);// ● 소유권을 해제(신호상태로 만든다.)
return 0;
}
void main()
{
DWORD threadID;
HANDLE hThread[2];
hMutex = CreateMutex( // ● 뮤텍스 생성 -> 핸들 저장
NULL, //보안 속성 // ●상속여부
FALSE, //초기 소유 // ●호출하는 쓰레드가 소유할지
"myMutex" // 이름 // ●아이디처럼 사용되는 문자열 이름. 유니크해야한다. (NULL 사용시 현 프로세스 내에서만 사용가능)
);
hThread[0] = CreateThread(0,0,ThreadFunc, (LPVOID)1, 0, &threadID);
hThread[1] = CreateThread(0,0,ThreadFunc, (LPVOID)2, 0, &threadID);
WaitForMultipleObjects(2, hThread, TRUE, INFINITE);
CloseHandle(hMutex); //제거
CloseHandle(hThread[0]);
CloseHandle(hThread[1]);
}
뮤텍스
#include <stdio.h>
#include <windows.h>
HANDLE hMutex;
DWORD __stdcall ThreadFunc(LPVOID pParam)
{
int num = (int)pParam;
printf("ThreadFunc Param : %d 생성\n", num);
WaitForSingleObject(hMutex, INFINITE);
WaitForSingleObject(hMutex, INFINITE);//2번 획득 //● 소유 카운트가 한 번 더 증가된다.
// 내부적으로 소유 카운트를 갖는다. 얻은만큼 받납을 해야한다.
for(int i = 0 ; i < 10 ; ++i)
{
printf("ThreadFunc[%d] : %d\n",num, i);
Sleep(200);
}
ReleaseMutex(hMutex); //1번 반환 //● 한 번만 릴리즈.
//ReleaseMutex(hMutex);
return 0;
}
void main()
{
DWORD threadID;
HANDLE hThread;
hMutex = CreateMutex(
NULL, //보안 속성
FALSE, //초기 소유
"myMutex" // 이름
);
hThread = CreateThread(0,0,ThreadFunc, (LPVOID)1, 0, &threadID);
Sleep(100); // 서브스레드가 뮤텍스를 먼저 소유하도록 Sleep() 사용
DWORD idx = WaitForSingleObject(hMutex, INFINITE); //● 반환값에 따라서 뮤텍스의 상태값을 볼 수 있다.
switch( idx )
{
case WAIT_OBJECT_0:
printf("정상적으로 뮤텍스 획득\n");
break;
case WAIT_ABANDONED:
printf("뮤텍스를 소유한 쓰레드가 Release하지 않고 종료함\n"); // 획득 수만큼 릴리즈 하지 않은 경우!
break;
case WAIT_TIMEOUT:
printf("설정 시간을 초과함\n"); // 타임 (INFINITE)를 1000 등으로 줄였을 때 발생
break;
case WAIT_FAILED:
printf("핸들이 유효하지 않음!\n"); // 핸들을 먼저 close 했을 경우
}
CloseHandle(hMutex);
CloseHandle(hThread);
}
이름있는 뮤텍스
#include <windows.h>
#include <TCHAR.H>
#include <vector>
using namespace std;
HANDLE hMutex;
struct TP { // 쓰레드 파라미터
HWND hwnd;
POINT pt; // 클릭한 좌표
};
DWORD __stdcall DrawThread(LPVOID pParam);
LRESULT CALLBACK WndProc(HWND hwnd, UINT iMsg,
WPARAM wParam, LPARAM lParam);
HINSTANCE g_hInst;
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpszCmdLine, int nCmdShow)
{
생략
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT iMsg,
WPARAM wParam, LPARAM lParam)
{
static vector<HANDLE> threadList;
TP* p;
HANDLE hThread;
static DWORD threadID;
switch (iMsg)
{
case WM_CREATE:
//hMutex = CreateMutex(NULL, FALSE, NULL);
hMutex = CreateMutex(NULL,FALSE,_T("Unique_Mutex")); // 이름있는 뮤텍스 -> 다른 프로세스에서도 적용된다.
break;
case WM_LBUTTONDOWN:
{
p = new TP;
TP& tp = *p;
tp.hwnd = hwnd;
tp.pt.x = LOWORD(lParam);
tp.pt.y = HIWORD(lParam);
hThread = CreateThread(NULL, 0, DrawThread, &tp, 0, &threadID);
threadList.push_back(hThread);
}
break;
case WM_RBUTTONDOWN:
{
static bool brun = true;
if (brun) {
for (unsigned i = 0; i < threadList.size(); i++)
{
SuspendThread(threadList[i]);
}
brun = false;
}
else {
for (unsigned i = 0; i < threadList.size(); i++)
{
ResumeThread(threadList[i]);
}
brun = true;
}
}
break;
case WM_DESTROY:
CloseHandle(hMutex);
PostQuitMessage(0);
for (unsigned i = 0; i < threadList.size(); i++)
CloseHandle(threadList[i]);
break;
}
return DefWindowProc(hwnd, iMsg, wParam, lParam);
}
DWORD __stdcall DrawThread(LPVOID pParam) { // Worker 쓰레드 라고한다.
Sleep(1000); // 공유 변수를 쓰게 되면 값을 늦게 가져와서 마우스 클릭이 더 빨랐을 경우 문제가 발생한다 (같은 좌표에 그림)
TP* p = (TP*)pParam;
HWND hwnd = p->hwnd;
HDC hdc = GetDC(hwnd);
POINT pt = p->pt;
while (1) {
WaitForSingleObject(hMutex, INFINITE);
for (int i = 0; i < 100; i += 5)
{
Rectangle(hdc, pt.x, pt.y, pt.x + i, pt.y + i);
Sleep(40);
}
ReleaseMutex(hMutex);
}
delete p;
ReleaseDC(hwnd, hdc);
return 0;
}
세마포어
#include <stdio.h>
#include <windows.h>
HANDLE hSema; // 세마포어
DWORD __stdcall ThreadFunc(LPVOID pParam)
{
int num = (int)pParam;
printf("ThreadFunc Param : %d 생성\n", num);
WaitForSingleObject(hSema, INFINITE);
for(int i = 0 ; i < 10 ; ++i)
{
printf("ThreadFunc[%d] : %d\n",num, i);
Sleep(200);
}
LONG previousCount;
ReleaseSemaphore(hSema, 1, &previousCount);//● 한개를 반환
return 0;
}
void main()
{
DWORD threadID;
HANDLE hThread;
hSema = CreateSemaphore(
NULL, //보안 속성
2, //초기 카운트 ● 2개의 쓰레드가 접근하능하게함 (열쇠의 수)
2, //최대 카운트 ● 세마포어가 지닐 수 있는 값의 최대 크기
"mySemaphore" // 이름
);
CloseHandle(CreateThread(0,0,ThreadFunc, (LPVOID)1, 0, &threadID));
CloseHandle(CreateThread(0,0,ThreadFunc, (LPVOID)2, 0, &threadID));
CloseHandle(CreateThread(0,0,ThreadFunc, (LPVOID)3, 0, &threadID));
CloseHandle(CreateThread(0,0,ThreadFunc, (LPVOID)4, 0, &threadID));
CloseHandle(CreateThread(0,0,ThreadFunc, (LPVOID)5, 0, &threadID));
getchar();
CloseHandle(hSema);
}
#include <windows.h>
#include <TCHAR.H>
#include <vector>
using namespace std;
HANDLE hSema; // 세마포어
struct TP { // 쓰레드 파라미터
HWND hwnd;
POINT pt; // 클릭한 좌표
};
DWORD __stdcall DrawThread(LPVOID pParam);
LRESULT CALLBACK WndProc(HWND hwnd, UINT iMsg,
WPARAM wParam, LPARAM lParam);
HINSTANCE g_hInst;
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpszCmdLine, int nCmdShow)
{
기본 코드 생략
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT iMsg,
WPARAM wParam, LPARAM lParam)
{
static vector<HANDLE> threadList;
TP* p;
HANDLE hThread;
static DWORD threadID;
switch (iMsg)
{
case WM_CREATE:
hSema = CreateSemaphore(NULL, 1, 3, NULL);
break;
case WM_RBUTTONDOWN:
{
static LONG prevCount;
ReleaseSemaphore(hSema, 1, &prevCount);
}
break;
case WM_LBUTTONDOWN:
{
p = new TP;
TP& tp = *p;
tp.hwnd = hwnd;
tp.pt.x = LOWORD(lParam);
tp.pt.y = HIWORD(lParam);
hThread = CreateThread(NULL, 0, DrawThread, &tp, 0, &threadID);
threadList.push_back(hThread);
}
break;
case WM_DESTROY:
CloseHandle(hSema);
PostQuitMessage(0);
for (unsigned i = 0; i < threadList.size(); i++)
CloseHandle(threadList[i]);
break;
}
return DefWindowProc(hwnd, iMsg, wParam, lParam);
}
DWORD __stdcall DrawThread(LPVOID pParam) { // Worker 쓰레드 라고한다.
Sleep(1000); // 공유 변수를 쓰게 되면 값을 늦게 가져와서 마우스 클릭이 더 빨랐을 경우 문제가 발생한다 (같은 좌표에 그림)
TP* p = (TP*)pParam;
HWND hwnd = p->hwnd;
HDC hdc = GetDC(hwnd);
POINT pt = p->pt;
while (1) {
WaitForSingleObject(hSema, INFINITE);
for (int i = 0; i < 100; i += 5)
{
Rectangle(hdc, pt.x, pt.y, pt.x + i, pt.y + i);
Sleep(40);
}
ReleaseSemaphore(hSema, 1, NULL);
}
delete p;
ReleaseDC(hwnd, hdc);
return 0;
}
-
이벤트
-
자동이벤트:비신호 상태로 자동 변환한다. 대기하고있었던 것이 여러개라도 하나만 대기가 풀린다.
-
수동이벤트:자동변환이 되지 않아서 비신호 상태를 꼭 ResetEvent() 함수로 비신호 상태로 만든다. 수동은 SetEvent()로 신호상태로 만드는 순간 대기를 하고 있던 것들이 모두 통과한다.
#include <stdio.h>
#include <windows.h>
HANDLE hEvent;
DWORD __stdcall ThreadFunc(LPVOID pParam)
{
int num = (int)pParam;
printf("ThreadFunc Param : %d 생성\n", num);
WaitForSingleObject(hEvent, INFINITE);
for(int i = 0 ; i < 10 ; ++i)
{
printf("ThreadFunc[%d] : %d\n",num, i);
Sleep(200);
}
return 0;
}
void main()
{
DWORD threadID;
HANDLE hThread;
//자동 이벤트: SetEvent()에 의해 Wait 함수들 중 하나만 리턴하고 다시 비신호 이벤트로.
hEvent = CreateEvent(
NULL, //보안 속성
TRUE, //수동 이벤트? TRUE일 경우 수동, FALSE 일경우 자동
FALSE, //초기 신호 상태
"myEvent" // 이름
);
CloseHandle(CreateThread(0,0,ThreadFunc, (LPVOID)1, 0, &threadID));
CloseHandle(CreateThread(0,0,ThreadFunc, (LPVOID)2, 0, &threadID));
CloseHandle(CreateThread(0,0,ThreadFunc, (LPVOID)3, 0, &threadID));
Sleep(10);
printf("엔터를 누르면 SetEvent() 호출!\n");
getchar();
SetEvent(hEvent);
printf("엔터를 누르면 SetEvent() 호출!\n");
getchar();
SetEvent(hEvent);
printf("엔터를 누르면 SetEvent() 호출!\n");
getchar();
SetEvent(hEvent);
getchar();
CloseHandle(hEvent);
}
PulseEvent : Set 과 ReSet을 동시에 한다.
(A,B)->C->D 4개 쓰레드가 돌고 있을 때. 어느 시점이 되면 결과치를 가져와 써야한다. A,B가 완료되었으면 C가 기다리다가 A 와 B의 신호를 기다린다. 그 다음 나머지 작업 완료후 -> D 에 전달 D는 기다리다가 신호가 오면 진행을 한다. (이러한 위상 작업에 이벤트를 사용한다.)
자동,수동 이벤트 어디에?
자동은 wait하는 놈 개수만큼 set을 해주어야한다.
3개의 쓰레드가 돌고있을 때 set을 설정하는 시점에
-
어느 누구도 wait를 호출x 경우 (Set->ReSet 펄스를 준것과 같다.)
-
일부만 wait 호출한 경우 (Set->(나머지 하나가 wait 할 수 있다.)->Reset [pulse는 set->reset을 바로한다.(싱크), 대기하던 쓰레드만 풀리고 도중 들어오는건 안된다.]
-
모든 참여자들이 wait를 하고 있을경우 () (Set->Rest 펄스를 준것과 같다.)
굳이 왜 2개를 만들어 놓았는가?
-
네트워크 플밍에서 내가원할 때 비신호,신호를 해야할 때가 온다. 비동기입출력 -> 컴플리션루틴(이벤트매커니즘)
#include <windows.h>
#include <TCHAR.H>
#include <vector>
using namespace std;
HANDLE hSema; // 세마포어
struct TP { // 쓰레드 파라미터
HWND hwnd;
POINT pt; // 클릭한 좌표
};
DWORD __stdcall DrawThread(LPVOID pParam);
LRESULT CALLBACK WndProc(HWND hwnd, UINT iMsg,
WPARAM wParam, LPARAM lParam);
HINSTANCE g_hInst;
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpszCmdLine, int nCmdShow)
{
g_hInst = hInstance;
기본 코드 생략
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT iMsg,
WPARAM wParam, LPARAM lParam)
{
static vector<HANDLE> threadList;
TP* p;
HANDLE hThread;
static DWORD threadID;
switch (iMsg)
{
case WM_CREATE:
hSema = CreateSemaphore(NULL, 0, 3, NULL);
break;
case WM_RBUTTONDOWN:
{
LONG prevCount;
ReleaseSemaphore(hSema, 1, &prevCount);
TCHAR buf[100];
wsprintf(buf, _T("Prev Count : [%d]"), prevCount);
SetWindowText(hwnd, buf);
}
break;
case WM_LBUTTONDOWN:
{
p = new TP;
TP& tp = *p;
tp.hwnd = hwnd;
tp.pt.x = LOWORD(lParam);
tp.pt.y = HIWORD(lParam);
hThread = CreateThread(NULL, 0, DrawThread, &tp, 0, &threadID);
threadList.push_back(hThread);
}
break;
case WM_DESTROY:
CloseHandle(hSema);
PostQuitMessage(0);
for (unsigned i = 0; i < threadList.size(); i++)
CloseHandle(threadList[i]);
break;
}
return DefWindowProc(hwnd, iMsg, wParam, lParam);
}
DWORD __stdcall DrawThread(LPVOID pParam) { // Worker 쓰레드 라고한다.
Sleep(1000); // 공유 변수를 쓰게 되면 값을 늦게 가져와서 마우스 클릭이 더 빨랐을 경우 문제가 발생한다 (같은 좌표에 그림)
TP* p = (TP*)pParam;
HWND hwnd = p->hwnd;
HDC hdc = GetDC(hwnd);
POINT pt = p->pt;
//while (1)
{
WaitForSingleObject(hSema, INFINITE);
for (int i = 0; i < 100; i += 5)
{
Rectangle(hdc, pt.x, pt.y, pt.x + i, pt.y + i);
Sleep(40);
}
//ReleaseSemaphore(hSema, 1, NULL);
}
delete p;
ReleaseDC(hwnd, hdc);
return 0;
}
● 플래그
'System Programming' 카테고리의 다른 글
[system programming] IPC 메세지, COPYDATASTRUCT (0) | 2019.07.05 |
---|---|
[system programming] 플래그, 자원 관리 클래스 (0) | 2019.07.05 |
[system programming] __declspec(thread) (0) | 2019.07.05 |
[system programming] 핸들, 커널 오브젝트, 프로세스 등 (0) | 2019.07.04 |
[system programming] WS_CHILD, SuspendThread, ResumeThread 등 (0) | 2019.07.04 |