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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
//Logger.h
namespace netlib
{
    class Logger
    {
    public:
        //ctors
        Logger();
 
        //dtors
        //graceful quit.
        virtual ~Logger();
 
        //make new log.
        void log(const std::string &entry);
    protected:
        void processEntries();
        bool exit = false;
        bool threadStarted = false;
        bool running = false;
        std::mutex mutex_lock;
        std::condition_variable conditionVariable;
        std::queue<std::string> stringQueue;
        std::thread backgroundThread;
        std::mutex mutexStarted;
        std::condition_variable conditioVariableStarted;
    private:
        //대입연산자 생성을 방지하기 위한 함수들
        //실제로 사용될 일이 없다.
        Logger(const Logger& src);
        Logger& operator=(const Logger& rhs);
    };
 
}
 
 
//Logger.cpp
#include "Logger.h"
static const  std::string dir = std::string("log/");
namespace netlib
{
    Logger::Logger()
    {
        //make&start background thread
 
        if (!running)
        {
            backgroundThread = std::thread{ &Logger::processEntries,this };
 
            //스레드가 처리 루프를 시작할 때까지 기다리자.
            std::unique_lock<std::mutex> lock(mutexStarted);
            conditioVariableStarted.wait(lock,
                [&]() {return threadStarted == true; }
            );
        }
        running = true;
    }
    Logger::~Logger()
    {
        //종료 전에 강제로 백그라운드 스레드를 깨우고,
        //그 스레드의 종료를 기다린다.
        //프로그램 종료시 log가 다 기록되지 않고 프로그램이 종료되는 것을
        //방지.
        exit = true;
        conditionVariable.notify_all();
        backgroundThread.join();
    }
    void Logger::log(const std::string &entry)
    {
        //락을 걸고 큐에 항목 삽입.
        std::unique_lock<std::mutex> lock(mutex_lock);
        stringQueue.push(entry);
        conditionVariable.notify_all();
    }
 
    void Logger::processEntries()
    {
        std::ofstream outFileStream(std::string(dir + "log.txt"));
        auto &ofs = outFileStream;
        if (ofs.fail())
        {
            std::cout << "Failed to open file" << std::endl;
            return;
        }
 
        printf("here\n");
        std::unique_lock<std::mutex> lock(mutex_lock);
 
        //스레드가 처리 루프를 시작했다고 알린다
        threadStarted = true;
        conditioVariableStarted.notify_all();
 
        //write logs
        while (true)
        {
            conditionVariable.wait(lock);
            
            lock.unlock();
            while (true//<-여기
            //여기서 stringqueue를 건드리면 안되는게
            //stringQueue가 점유되지 않았으므로..
            {
                lock.lock();
                if (stringQueue.empty())
                    break;
                else if( stringQueue.size()>=5)
                {
                    for (int i = 0; i < 5;++i)
                    {
                        ofs << stringQueue.front() << std::endl;
                        std::cout << stringQueue.front() << std::endl;
                        stringQueue.pop();
                    }
                }
                else
                {
                    for (int i = 0; i < stringQueue.size(); ++i)
                    {
                        ofs << stringQueue.front() << std::endl;
                        std::cout << stringQueue.front() << std::endl;
                        stringQueue.pop();
                    }
                }
                lock.unlock();
            }
            if (exit) break//종료.
        }
    }
 
}
 
 
//main(unittest.cpp)
 
#include "Logger.h"
#include <iostream>
#include <memory>
#include <string>
#include <sstream>
using netlib::Logger;
using namespace std;
void logSomeMEssages(int id, Logger& logger)
{
    for (int i = 0; i < 10++i)
    {
        stringstream ss;
        ss << "log entry " << i << "from thread" << id;
        logger.log(ss.str());
    }
}
int main()
{
    Logger log;
    vector<thread> threads;
    for (int i = 0; i < 10++i)
        threads.push_back(thread{ logSomeMEssages,i,ref(log) });
    //여기 ref는 log를 참조로 던진다는 얘기.
    //중요하다.
    //로그는 복사되면 안된다.
 
    for (auto &t : threads)
        t.join();
    return 0;
}
 
 
cs


dd