Thinking Different








Synchronization Patterns : Thread Safe Interface - 동기화 패턴 : 스레드 안전 인터페이스


  • 멀티스레드 프로그래밍 중에서 클래스 내부의 메서드 호출로 발생되는 셀프 데드락(Self-Dead lock) 현상 방지를 위한 패턴이다.
  • 동기화에 필요한 공개함수와 내부에서 사용될 내부 함수를 분리하고, 외부에서 접근하는 공개함수들에 대해서만 동기화하고 내부 함수를 세분화하여 접근하도록 한다.



동기화와 셀프 데드락(Self-Dead lock)이란?


  • 동기화 : 다수의 스레드가 하나의 리소스에 대해서 동시에 접근할 경우 다른 스레드가 변경한 리소스를 또 다른 스레드가 덮어쓰거나 변경시켜버릴 가능성이 있다(Race condition)이를 보호하기 위해서 동기화라는 작업을 통해서 적절히 공유된 리소스를 접근할때에는 하나의 스레드만 접근되도록 하는 것이다.
  • 데드락 : 멀티 스레드 환경에서 어떤 스레드도 리소스에 더이상 접근이 불가능한 상태를 일컫는다.
  • 셀프 데드락 : 멀티 스레드 환경에서 어떤 스레드가 이미 자신이 선점한 리소스 동기화 락을 다시 얻으려고 할때 발생된다.






Self-Dead lock 발생 코드 예제)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#include <list>
 
class CTest
{
public:
    CTest(int i) : m_Id(i) {}
    int GetId() const { return m_Id; }
private:
    int m_Id;
};
 
class CTestManager : public CMultiThreadSync<CTestManager>
{
public:
    ~CTestManager()
    {
        for each (CTest* var in mList)
            delete var;
        mList.clear();
    }
public:
    void insert(CTest* i)
    {
        CThreadSync cs;        // 첫번째 스코프 동기화
        if (!search(i))
            mList.push_back(i);
        else
            delete i;
    }
 
    bool search(CTest* i)
    {
        CThreadSync cs;     // 두번째 스코프 동기화
        for each (CTest* var in mList)
        {
            if (var->GetId() == i->GetId())
                return true;
        }
        return false;
    }
private:
    list<CTest*> mList;
};
 


  1. Insert 함수를 통해 CTest 객체를 추가한다.
  2. Insert 함수 내부에서 먼저 멀티스레드 락 방지로 스코프 동기화를 해주고, 컨테이너에 객체가 이미 존재하는지 검사 후 없다면 추가하도록 한다.
  3. 여기서 문제점은 최초 insert 함수 호출시 스코프 동기화를 걸고 락이 해제되기 전에 search 함수 호출로 다시 내부에서 동기화 락을 얻는 과정에서 셀프 데드락이 발생된다.




수정 코드 다이어그램)



다이어그램에서 보듯이, insert 와 search함수들을 외부함수, 내부함수로 세분화하고 외부함수에만 스코프 동기화를 걸어주어 셀프 데드락을 해결하였다.


수정 소스 코드)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
#include <list>
 
//------------------------------------------------------------------
// CTest Entity 클래스
class CTest
{
public:
    CTest(int i) : m_Id(i) {}
    int GetId() const { return m_Id; }
 
private:
    int m_Id;
};
 
//------------------------------------------------------------------
// CTestManager 클래스 <스코프 동기화>
class CTestManager : public CMultiThreadSync<CTestManager>
{
public:
    CTestManager() {}
    ~CTestManager()
    {
        for each (CTest* var in mList)
            delete var;
 
        mList.clear();
    }
 
public:
    void insert(CTest* i)
    {
        CThreadSync cs;
        insert_i(i);
    }
 
    bool search(CTest* i)
    {
        CThreadSync cs;
        return search_i(i);
    }
 
private:
    void insert_i(CTest* i)
    {
        if (!search_i(i))
            mList.push_back(i);
    }
 
    bool search_i(CTest* i)
    {
        for each (CTest* var in mList)
        {
            if (var->GetId() == i->GetId())
                return true;
        }
 
        return false;
    }
 
private:
    list<CTest*> mList;
};
 
//------------------------------------------------------------------
// Main
int _tmain(int argc, _TCHAR* argv[])
{
    CTestManager mTestManager;
    mTestManager.insert(new CTest(1024));
 
    return 0;
}