둘 이상의 스레드가 공유 자원에 접근할 때, 오직 한 스레드만 접근을 허용해야 하는 경우에 사용한다. 일반 동기화 객체와 달리 개별 프로세스의 유저(user) 메모리 영역에 존재하는 단순한 구조체이다. 따라서 다른 프로세스가 접근할 수 없으므로 한 프로세스에 속한 스레드 간 동기화에만 사용한다. 일반 동기화 객체보다 빠르고 효율적이다.
UserMode에서만 작동 해서 성능 측면에서 빠르다. 하지만 해당 방법은 하나의 프로세스, 하나의 프로세스에서의 Thread에서만 사용 가능 하다.
Mutex라는 커널 객체를 이용해서 동기화를 하는 방법이다. 커널 객체를 사용하기 때문에 프로세스 사이에서의 동기화도 가능하다. 물론 쓰레드 사이에서의 동기화도 가능하다.
세마포어 역시 커널 객체를 이용한 동기화 방법이다.지정된 쓰레드 갯수 만큼 동시에 자원에 접근 할 수있도록 하는 것이다.멀트 쓰레드 프로그래밍을 할 때 쓰레드를 무제한 생성 같은 경우에 제한 할 수 있을 거같다.
이번 예제는 크리티컬 섹션을 사용해서 하나의 스레드만 임계영역에서 자원에 접근할 수 있게 함
#pragma once
#include <afxwin.h>
#include <afxmt.h>
class CThread : public CWinThread
{
DECLARE_DYNCREATE(CThread)
protected:
CThread();
virtual ~CThread();
public:
virtual BOOL InitInstance();
virtual int ExitInstance();
void SetThreadNum(int num);
void SetMainWnd(CWnd* pWnd);
protected:
DECLARE_MESSAGE_MAP()
private:
int m_threadNum;
CWnd* m_pMainWnd; // Main window pointer for communication
LONG m_nCounter;
bool isCs;
static const LONG g_nTarget;
static CCriticalSection g_cs;
static int g_nFlagCounter;
};
#include "pch.h"
#include "CThread.h"
IMPLEMENT_DYNCREATE(CThread, CWinThread)
CThread::CThread() : m_threadNum(0), m_pMainWnd(nullptr), m_nCounter(0)
{
}
CThread::~CThread()
{
}
BOOL CThread::InitInstance()
{
while (true)
{
++m_nCounter;
if (!isCs && m_nCounter > (g_nTarget - 300))
{
CString msg;
msg.Format(_T("%d번 쓰레드: 임계영역 진입"), m_threadNum);
if (m_pMainWnd)
{
m_pMainWnd->PostMessage(WM_USER + 1, (WPARAM)new CString(msg));
}
isCs = TRUE;
g_cs.Lock();
}
if (m_nCounter > g_nTarget)
{
CString msg;
msg.Format(_T("%d번 쓰레드: 목표치 도달 (완료 스레드: %d/3)"), m_threadNum, ++g_nFlagCounter);
if (m_pMainWnd)
{
m_pMainWnd->PostMessage(WM_USER + 1, (WPARAM)new CString(msg));
}
g_cs.Unlock();
if (g_nFlagCounter == 3) {
CString msg;
msg.Format(_T("완료"));
m_pMainWnd->PostMessage(WM_USER + 1, (WPARAM)new CString(msg));
}
break;
}
if (m_pMainWnd)
{
CString* pStr = new CString;
pStr->Format(_T("Thread %d: %ld"), m_threadNum, m_nCounter);
m_pMainWnd->PostMessage(WM_USER + 1, (WPARAM)pStr);
}
Sleep(1);
}
return TRUE;
}
int CThread::ExitInstance()
{
return CWinThread::ExitInstance();
}
void CThread::SetThreadNum(int num)
{
m_threadNum = num;
}
void CThread::SetMainWnd(CWnd* pWnd)
{
m_pMainWnd = pWnd;
}
const LONG CThread::g_nTarget = 500;
CCriticalSection CThread::g_cs;
bool isCs = false;
int CThread::g_nFlagCounter = 0;
BEGIN_MESSAGE_MAP(CThread, CWinThread)
END_MESSAGE_MAP()
class CDThreadBox : public CDialogEx
{
DECLARE_DYNAMIC(CDThreadBox)
public:
CDThreadBox(CWnd* pParent = nullptr); // 표준 생성자입니다.
virtual ~CDThreadBox();
#ifdef AFX_DESIGN_TIME
enum { IDD = IDD_DIALOG_THREAD };
#endif
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 지원입니다.
DECLARE_MESSAGE_MAP()
public:
afx_msg void OnStnClickedStatic1();
afx_msg void OnStnClickedStatic2();
afx_msg void OnStnClickedStatic3();
afx_msg LRESULT OnUpdateCounter(WPARAM wParam, LPARAM lParam);
afx_msg LRESULT OnThreadMessage(WPARAM wParam, LPARAM lParam);
private:
CStatic m_thread_box1;
CStatic m_thread_box2;
CStatic m_thread_box3;
CStatic m_result_box;
};
#include "pch.h"
#include "ThreadSample.h"
#include "afxdialogex.h"
#include "CDThreadBox.h"
#include "CThread.h"
IMPLEMENT_DYNAMIC(CDThreadBox, CDialogEx)
CDThreadBox::CDThreadBox(CWnd* pParent /*=nullptr*/)
: CDialogEx(IDD_DIALOG_THREAD, pParent)
{
}
CDThreadBox::~CDThreadBox()
{
}
void CDThreadBox::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
DDX_Control(pDX, IDC_STATIC_1, m_thread_box1);
DDX_Control(pDX, IDC_STATIC_2, m_thread_box2);
DDX_Control(pDX, IDC_STATIC_3, m_thread_box3);
DDX_Control(pDX, IDC_STATIC_4, m_result_box);
}
BEGIN_MESSAGE_MAP(CDThreadBox, CDialogEx)
ON_STN_CLICKED(IDC_STATIC_1, &CDThreadBox::OnStnClickedStatic1)
ON_STN_CLICKED(IDC_STATIC_2, &CDThreadBox::OnStnClickedStatic2)
ON_STN_CLICKED(IDC_STATIC_3, &CDThreadBox::OnStnClickedStatic3)
ON_MESSAGE(WM_USER + 1, &CDThreadBox::OnUpdateCounter)
END_MESSAGE_MAP()
void CDThreadBox::OnStnClickedStatic1()
{
CThread* pThread = (CThread*)AfxBeginThread(RUNTIME_CLASS(CThread));
pThread->SetThreadNum(1);
pThread->SetMainWnd(this);
}
void CDThreadBox::OnStnClickedStatic2()
{
CThread* pThread = (CThread*)AfxBeginThread(RUNTIME_CLASS(CThread));
pThread->SetThreadNum(2);
pThread->SetMainWnd(this);
}
void CDThreadBox::OnStnClickedStatic3()
{
CThread* pThread = (CThread*)AfxBeginThread(RUNTIME_CLASS(CThread));
pThread->SetThreadNum(3);
pThread->SetMainWnd(this);
}
LRESULT CDThreadBox::OnThreadMessage(WPARAM wParam, LPARAM lParam)
{
//CString* pMsg = (CString*)wParam;
//CString currentText;
//m_thread_box1.GetWindowText(currentText);
//currentText += _T("\r\n") + *pMsg;
//m_thread_box1.SetWindowText(currentText);
//delete pMsg;
return 0;
}
LRESULT CDThreadBox::OnUpdateCounter(WPARAM wParam, LPARAM lParam)
{
CString* pStr = (CString*)wParam;
if (pStr->Find(_T("Thread 1:")) == -1 &&
pStr->Find(_T("Thread 2:")) == -1 &&
pStr->Find(_T("Thread 3:")) == -1)
{
CString currentText;
m_result_box.GetWindowText(currentText);
currentText += _T("\r\n") + *pStr;
m_result_box.SetWindowText(currentText);
}
else
{
if (pStr->Find(_T("Thread 1:")) != -1)
{
m_thread_box1.SetWindowText(*pStr);
}
else if (pStr->Find(_T("Thread 2:")) != -1)
{
m_thread_box2.SetWindowText(*pStr);
}
else if (pStr->Find(_T("Thread 3:")) != -1)
{
m_thread_box3.SetWindowText(*pStr);
}
}
delete pStr;
return 0;
}
각각 클릭이 아닌 스레드 3개를 동시에 시작할 방법은 비동기적으로 한번에 이벤트를 발생시켜야 가능할 것이다.
그럼 PostMessage를 사용해야하고 메시지를 직접 보내 핸들러를 작동시켜야만 한다.
LRESULT CDThreadBox::OnStaticClicked1(WPARAM wParam, LPARAM lParam)
{
OnStnClickedStatic1();
return 0;
}
LRESULT CDThreadBox::OnStaticClicked2(WPARAM wParam, LPARAM lParam)
{
OnStnClickedStatic2();
return 0;
}
LRESULT CDThreadBox::OnStaticClicked3(WPARAM wParam, LPARAM lParam)
{
OnStnClickedStatic3();
return 0;
}
ON_MESSAGE(WM_USER_STATIC_CLICKED1, &CDThreadBox::OnStaticClicked1)
ON_MESSAGE(WM_USER_STATIC_CLICKED2, &CDThreadBox::OnStaticClicked2)
ON_MESSAGE(WM_USER_STATIC_CLICKED3, &CDThreadBox::OnStaticClicked3)
동기적이고 순차적으로 실행된다.
비동기적이면서 비순차적이다.
void CDThreadBox::OnStnClickedStatic5()
{
SendMessage(WM_USER_STATIC_CLICKED1);
Sleep(1);
SendMessage(WM_USER_STATIC_CLICKED2);
Sleep(1);
SendMessage(WM_USER_STATIC_CLICKED3);
}
void CDThreadBox::OnStnClickedStatic5()
{
PostMessage(WM_USER_STATIC_CLICKED1);
Sleep(1);
PostMessage(WM_USER_STATIC_CLICKED2);
Sleep(1);
PostMessage(WM_USER_STATIC_CLICKED3);
}
안녕하세요
MFC 동기화 관련해서 공부중이였는데 도움이 많이 됐습니다. 감사합니다.
실례가 안된다면 실습 예제 솔루션 파일을 받아볼 수 있을까요?