티스토리 뷰
무텍스(Mutex)는 MUTual EXclusion(상호배제)의 의미이며, 특정 시점에 프로세스 또는 스레드 간에 특정 자원이나 역할에 대해 우선권을 갖는 문제를 교통 정리하기 위해서 시스템에서 제공하는 자원 입니다. 유닉스/리눅스나 윈도우 모두 대표적인 IPC(Inter Process Communication) 자원으로 공유메모리, 메시지 큐, 세마포어를 제공하는데 세마포어와 유사한 개념이라고 할 수 있습니다.
자원의 우선권을 획득하기 위해 줄서고, 자원 사용이 끝나면 반환하는 형태는 Mutex나 세마포어나 유사하다고 볼 수 있습니다. 여러 프로세스나 스레드가 동시에 작업을 할 수 없는(동시에 작업을 한다면 문제가 생기는...) 부분에 Mutex를 배치함으로서 시스템 전체적으로 정확한 작업을 할 수 있게 되는 것입니다.
■ 동시 작업으로 문제가 생길 수 있는 상황
현실에서 동시에 작업을 하면 문제가 생기는 상황은 여러가지가 있을 수 있는데 한두가지 예를 든다면 아래와 같습니다.
은행 A계좌에 동시에 홍길동은 입금을 하고, 홍길순은 출금을 한다고 가정합니다.
홍길동이 1000원을 입금하려는 시점의 잔액이 5000원이라면,
입금과정은 기존 잔액 읽기 > 1000 더하기 > 입금후 잔액 저장으로 구성되고
출금과정은 기존 잔액 읽기 > 2000 빼기 > 출금후 잔액 저장으로 구성되는데
각각의 세부 과정에서 거래후 잔액 저장 이전 과정이 겹치면 동시 작업 결과는 정상적인 상황을 벗어나 버립니다.
이런 SELECT for UPDATE 상황을 대비하기 위해서 일반적으로 데이터베이스에서는 BEGIN....COMMIT으로 Two-Phase Commit을 지원해서 오류를 방지 합니다. 위의 상황은 한쪽이 먼저 A계좌에 대한 우선권을 얻어 작업을 시작하고 우선권을 얻지 못한 다른쪽은 A계좌에 대한 권한을 얻기 위해 대기하다가 먼저 우선권을 가진 쪽에서 권한을 내놓으면 그때 작업을 시작함이 오류를 막는 방법 입니다.
또 한가지 예를 든다면 오늘의 경우처럼 응용 프로그램을 중복 실행하는 문제를 막는 것입니다. 워치독(Watchdog) 프로그램은 다른 응용 프로그램들의 동작 여부를 감시하다가 혹시 프로그램이 죽으면 자동으로 살리는 역할을 담당하게 하는 것인데, 이런 프로그램이 2개 이상 동작하고 있다면 한번에 2개 이상의 프로그램을 동작시키는 비정상적인 상황을 초래하게 되는 것입니다. 이때도 한 프로그램이 살아있다면, 동작중에는 계속 우선권을 가지고 있다가 프로그램 종료시에 우선권을 내놓아 다른 프로그램이 동작할 수 있도록 해주는 것입니다. 물론 Mutex 사용이 유일한 방법은 아니지만...
■ DBMS에서 실제로 Mutex를 사용하는 코드 예제
아래의 코드는 PostgreSQL DBMS의 일부 코드 입니다. CreateMutex 함수 사용을 눈여겨 봅니다.
#ifdef WIN32 #ifdef ENABLE_THREAD_SAFETY void win32_pthread_mutex(volatile pthread_mutex_t *mutex) { if (mutex->handle == NULL) { while (InterlockedExchange((LONG *) &mutex->initlock, 1) == 1) Sleep(0); if (mutex->handle == NULL) mutex->handle = CreateMutex(NULL, FALSE, NULL); InterlockedExchange((LONG *) &mutex->initlock, 0); } } static pthread_mutex_t win32_pthread_once_lock = PTHREAD_MUTEX_INITIALIZER; void win32_pthread_once(volatile pthread_once_t *once, void (*fn) (void)) { if (!*once) { pthread_mutex_lock(&win32_pthread_once_lock); if (!*once) { *once = true; fn(); } pthread_mutex_unlock(&win32_pthread_once_lock); } } #endif /* ENABLE_THREAD_SAFETY */ #endif /* WIN32 */
■ C++ 응용 프로그램에서 중복 실행 방지에 Mutex 응용하기
1. 응용 프로그램의 진입점을 찾습니다.
InitInstance를 검색하여 다이얼로그를 띄우는 등의 본격적인 로직 이전에 코드를 추가해야 합니다.
2. 진입점(InitInstance)에 해당하는 클래스(*.h)에 Mutex 핸들을 정의 합니다. 이때 ExitInstance 함수 정의가 없으면 추가합니다.
protected:
HANDLE m_hMutex;
public:
virtual BOOL InitInstance();
virtual int ExitInstance();
3. 진입점(InitInstance)의 본격적인 코드 이전에 Mutex 생성 로직을 추가하고, 우선권을 얻지 못할때의 메시지도 추가 합니다. 예제에서는 Mutex의 이름으로 응용프로그램의 이름을 설정 했습니다.
m_hMutex = ::CreateMutex(FALSE, 0, "test_WATCHDOG");
if (::GetLastError() == ERROR_ALREADY_EXISTS)
{
AfxMessageBox ( "Error! Task already started!" );
exit(0);
}
4. 프로그램 종료점에 우선권을 돌려주는 로직을 추가 합니다. 종료점 함수가 없으면 추가 합니다.
int CTestApp::ExitInstance()
{
::ReleaseMutex(m_hMutex);
return CWinApp::ExitInstance();
}
■ C# 응용 중복 실행 방지에 Mutex 활용하기
1. 응용 프로그램의 진입점을 찾습니다. C#은 Program.cs의 static void Main() 입니다.
2. using System.Threading;을 추가하고 Application.Run이 있는 블록을 아래 코드와 같이 Mutex 생성 여부를 판단하여 우선권을 얻는 경우에만 실행하도록 조정하고 Application.Run다음에 ReleaseMutex()를 배치하면 됩니다.
static void Main()
{
bool flagMutex;
Mutex m_hMutex = new Mutex(true, "TESTAPP", out flagMutex);
if (flagMutex)
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new TESTAPP());
m_hMutex.ReleaseMutex();
}
else
{
MessageBox.Show("Task already started!", "Error!");
}
}
'프로그래밍' 카테고리의 다른 글
SQLite 개발 환경 준비와 C#에서 사용하기 (0) | 2019.03.12 |
---|---|
디렉토리 내용을 구조화해서 출력하기 (0) | 2019.03.11 |
C# 딕셔너리 값으로 키찾기 (0) | 2018.12.12 |
C# 배열 초기화 방법 (0) | 2018.12.12 |
C#으로 데이터베이스 연결 스트링에서 비밀번호 숨기기, C#에서 정규식 사용하기 (0) | 2018.12.05 |