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
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
// MutexTest.cpp: 콘솔 응용 프로그램의 진입점을 정의합니다.
//
 
#include "stdafx.h"
 
#include <iostream>
#include <Windows.h>
#include <thread>
#include <mutex>
 
#include <time.h>
 
int g_index = 1000;
 
int operateCount = 0;
 
#include <list>
 
 
static std::recursive_mutex rMutex;
 
void Increase(HANDLE hMutex) {
    std::unique_lock<std::recursive_mutex> lock(rMutex, std::defer_lock); // 초기화 시간을 제외하기 위해
 
    for (int i = 0; i < 100000; i++) {
        WaitForSingleObject(hMutex, INFINITE);
        //g_index++;
 
        operateCount++;
        
        
        ReleaseMutex(hMutex);
    }
 
}
 
void Decrease(HANDLE hMutex) {
    std::unique_lock<std::recursive_mutex> lock(rMutex, std::defer_lock); // 초기화 시간을 제외하기 위해
 
    for (int i = 0; i < 100000; i++) {
        WaitForSingleObject(hMutex, INFINITE);
        
        operateCount++;
        
 
        ReleaseMutex(hMutex);
    }
}
 
 
//static std::recursive_mutex rMutex;
 
//std::unique_lock<std::recursive_mutex> lock(rMutex, std::defer_lock);
 
void Increase2() {
    std::unique_lock<std::recursive_mutex> lock(rMutex, std::defer_lock);
 
    for (int i = 0; i < 100000; i++){
 
        lock.lock();
        operateCount++;
 
 
        lock.unlock();
    }
 
}
 
void Decrease2() {
 
    std::unique_lock<std::recursive_mutex> lock(rMutex, std::defer_lock);
    for (int i = 0; i < 100000; i++) {
        lock.lock();
        operateCount++;
        lock.unlock();
    }
 
}
 
 
 
static std::chrono::system_clock::time_point start;
 
int main()
{
 
    HANDLE hMutex = CreateMutex(NULL, FALSE, NULL);
    
    //std::unique_lock<std::recursive_mutex> lock(rMutex, std::defer_lock);
 
    //매개 변수로 넘기려고 했으나,, 복제가 안된다고 한다
 
    /*
        CreateMutex VS unique_lock을 통한 recursiveMutex의
        시간 차이를 비교해 본결 과,
        전자는 대략 1초
        후자는 대략 0.08초가 걸렸다.
        즉 시간차이는 10배 이상 차이가 난다.
        전자는 좀 더 세밀한 동기화를 위해 필요하고
        후자는 세밀한 동기화가 필요없고 원자성만 보장하고 싶을 때
        사용하는 것이 적합할 것 같다.
    
    */
    
    start = std::chrono::system_clock::now();
 
//    std::thread t1(Increase, hMutex);
//    std::thread t2(Decrease, hMutex);
 
 
    std::thread t1(Increase2);
    std::thread t2(Decrease2);
 
    t1.join();
    t2.join();
 
    std::chrono::duration<double> sec = std::chrono::system_clock::now() - start;
 
    std::cout << "수행 시간 : " << sec.count() << std::endl;
    //std::chrono::seconds duration(1);
    //std::this_thread::sleep_for(duration);
    
    printf("operate Count : %d\n", operateCount);
    
    getchar();
    return 0;
}
 
 
cs



recursive_mutex 와 winapi를 통해 만드는 mutex와  둘 중에 뭘 쓸까 고민하다가


비교를 해보기로 했다.


결과는 코드에 써있다.  


CreateMutex VS unique_lock을 통한 recursiveMutex의


시간 차이를 비교해 본결 과,


전자는 대략 1초


후자는 대략 0.08초가 걸렸다.


즉 시간차이는 10배 이상 차이가 난다.


전자는 좀 더 세밀한 동기화를 위해 필요하고


후자는 세밀한 동기화가 필요없고 원자성만 보장하고 싶을 때


사용하는 것이 적합할 것 같다.




나는 OverLapped 구조체를 상속 받은 구조체나 클래스에 대해서 초기화를 할 때


생성자에 memset(this, NULL, sizeof(*this)) 로 초기화를 하고 있었는데.. 이 방법은 상당히 편하다..


다른 멤버 변수에 굳이 지정을 해주지 않아도 한 줄로 끝내버릴 수 있는 강력함을 가지고 있다.


1
2
3
4
5
6
7
8
9
10
struct Data : public OVERLAPPED {
 
     int a;
    char buf[10];
 
    Data(){
        memset(thisNULLsizeof(*this));
    }
 
}
cs


이렇게 말이다..


그런데 이 방법을 즐겨 쓰다보면 생각보다 위험성이 크고 이러한 방법에 대해서 문제가 발생할 시 아무리 오류가 나는 지점에서 디버깅을 하고 또하고 또또또또또또 해봤자 이유를 못찾을 것이다..


때문에 이런 방법을 썼을 경우 나타날 수 있는 문제점에 대해서 서술하고자 한다.


1. 멤버이니셜라이즈..


1
2
3
4
5
6
7
8
9
10
11
12
13
 
 
class test {
 
 
    SOCKET sock;
 
    test(SOCKET sock) : sock(sock){
 
        memset(thisNULLsizeof(*this));
    }
 
};
cs


가지고 있던 sock 이 날라가버린다. 이니셜라이즈로 할당을 한 것을 날려버리는 셈이 되는 것이다..


2. 가상함수를 상속 받은 클래스 초기화


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

class test {
public:
    test(){
    }
    virtual void hello() { };   
};
 
 
class test2 {
 int a;
public:
    test2(){
        memset(thisNULLsizeof(*this));
    }
 
    void hello() { printf("Hello"); }
}
cs


test2 는 몇 바이트를 가지고 있을까 ? 


==>> sizeof(test2) 는 8 바이트이다... int a 는 4바이트를 가지고 있지만 나머지 4바이트는 어디서 나온걸까..


바로 가상함수 테이블이다..  가상함수를 상속받게 되면 자식은 가상함수 테이블을 가져가게 된다.


때문에 자식 객체에서 생성자부분에서 memset으로 크기만큼 초기화 해버리면 가상함수테이블의 내용까지 날려버리기 때문에


후에 test2를 통해 hello 라는 함수에 대해서 접근할 때 접근 할 수 없게 된다..


3. stl .. set , unorderedset  사용시 문제점..


위와 같이 std::set을 멤버 변수로 가지고 있는 클래스에서 생성자에서 memset 을 초기화 하니 set에서는 Root 오류가 뜨고 unorderedset 에서는 outofrange 같은 오류가 난다..



http://snowdeer.github.io/c++/2017/08/18/cpp11-thread-example/


클래스 메서드를 이용할 때랑 일반 함수를 이용할 때랑 다른점이 있었는데 삽질하고 있다가 발견한 오아시스

+ Recent posts