Thinking Different




아래는 윈도우 스레드 풀을 사용한 간단한 예제입니다.


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
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
//
// Thread pool timer callback function template
//
VOID CALLBACK MyTimerCallback( PTP_CALLBACK_INSTANCE Instance, PVOID Parameter, PTP_TIMER Timer )
{
    // Instance, Parameter, and Timer not used in this example.
    UNREFERENCED_PARAMETER(Instance);
    UNREFERENCED_PARAMETER(Parameter);
    UNREFERENCED_PARAMETER(Timer);
 
    //
    // Do something when the timer fires.
    //
    _tprintf(_T("MyTimerCallback: timer has fired.\n"));
}
 
 
//
// This is the thread pool work callback function.
//
VOID CALLBACK MyWorkCallback( PTP_CALLBACK_INSTANCE Instance, PVOID Parameter, PTP_WORK Work )
{
    // Instance, Parameter, and Work not used in this example.
    UNREFERENCED_PARAMETER(Instance);
    UNREFERENCED_PARAMETER(Parameter);
    UNREFERENCED_PARAMETER(Work);
 
    //
    // Do something when the work callback is invoked.
    //
    _tprintf(_T("MyWorkCallback: Task performed.\n"));
 
    return;
}
 
VOID DemoCleanupPersistentWorkTimer()
{
    BOOL bRet = FALSE;
    PTP_WORK work = NULL;
    PTP_TIMER timer = NULL;
    PTP_POOL pool = NULL;
    PTP_WORK_CALLBACK workcallback = MyWorkCallback;
    PTP_TIMER_CALLBACK timercallback = MyTimerCallback;
    TP_CALLBACK_ENVIRON CallBackEnviron;
    PTP_CLEANUP_GROUP cleanupgroup = NULL;
    FILETIME FileDueTime;
    ULARGE_INTEGER ulDueTime;
    UINT rollback = 0;
 
    try
    {
        InitializeThreadpoolEnvironment(&CallBackEnviron);
 
        //
        // Create a custom, dedicated thread pool.
        //
        pool = CreateThreadpool(NULL);
 
        if (NULL == pool) 
        {
            _tprintf(_T("CreateThreadpool failed. LastError: %u\n"), GetLastError());
            throw 1;
        }
 
        //
        // The thread pool is made persistent simply by setting
        // both the minimum and maximum threads to 1.
        //
        SetThreadpoolThreadMaximum(pool, 1);
 
        bRet = SetThreadpoolThreadMinimum(pool, 1);
 
        if (FALSE == bRet) 
        {
            _tprintf(_T("SetThreadpoolThreadMinimum failed. LastError: %u\n"), GetLastError());
            throw 1;
        }
 
        //
        // Create a cleanup group for this thread pool.
        //
        cleanupgroup = CreateThreadpoolCleanupGroup();
 
        if (NULL == cleanupgroup) 
        {
            _tprintf(_T("CreateThreadpoolCleanupGroup failed. LastError: %u\n"), GetLastError());
            throw 1;
        }
 
        //
        // Associate the callback environment with our thread pool.
        //
        SetThreadpoolCallbackPool(&CallBackEnviron, pool);
 
        //
        // Associate the cleanup group with our thread pool.
        // Objects created with the same callback environment
        // as the cleanup group become members of the cleanup group.
        //
        SetThreadpoolCallbackCleanupGroup(&CallBackEnviron, cleanupgroup, NULL);
 
        //
        // Create work with the callback environment.
        //
        work = CreateThreadpoolWork(workcallback, NULL, &CallBackEnviron);
 
        if (NULL == work) 
        {
            _tprintf(_T("CreateThreadpoolWork failed. LastError: %u\n"), GetLastError());
            throw 1;
        }
 
        //
        // Submit the work to the pool. Because this was a pre-allocated
        // work item (using CreateThreadpoolWork), it is guaranteed to execute.
        //
        SubmitThreadpoolWork(work);
 
 
        //
        // Create a timer with the same callback environment.
        //
        timer = CreateThreadpoolTimer(timercallback, NULL, &CallBackEnviron);
    
        if (NULL == timer) 
        {
            _tprintf(_T("CreateThreadpoolTimer failed. LastError: %u\n"), GetLastError());
            throw 1;
        }
 
        //
        // Set the timer to fire in one second.
        //
        ulDueTime.QuadPart = (ULONGLONG) -(1 * 10 * 1000 * 1000);
        FileDueTime.dwHighDateTime = ulDueTime.HighPart;
        FileDueTime.dwLowDateTime  = ulDueTime.LowPart;
 
        SetThreadpoolTimer(timer, &FileDueTime, 0, 0);
 
        //
        // Delay for the timer to be fired
        //
        Sleep(1500);
 
        //
        // Wait for all callbacks to finish.
        // CloseThreadpoolCleanupGroupMembers also releases objects
        // that are members of the cleanup group, so it is not necessary 
        // to call close functions on individual objects 
        // after calling CloseThreadpoolCleanupGroupMembers.
        //
        CloseThreadpoolCleanupGroupMembers(cleanupgroup, FALSE, NULL);
    }
    catch( ... )
    {
        _tprintf( _T("Error DemoNewRegisterWait\n") );
    }
 
    if( cleanupgroup != NULL )
    {
        // Clean up the cleanup group members.
        CloseThreadpoolCleanupGroupMembers(cleanupgroup, FALSE, NULL);
 
        // Clean up the cleanup group.
        CloseThreadpoolCleanupGroup(cleanupgroup);
    }
 
    if( pool != NULL )
        // Clean up the pool.
        CloseThreadpool(pool);
}
 
//
// Thread pool wait callback function template
//
VOID CALLBACK MyWaitCallback( PTP_CALLBACK_INSTANCE Instance, PVOID Parameter, PTP_WAIT Wait, TP_WAIT_RESULT WaitResult )
{
    // Instance, Parameter, Wait, and WaitResult not used in this example.
    UNREFERENCED_PARAMETER(Instance);
    UNREFERENCED_PARAMETER(Parameter);
    UNREFERENCED_PARAMETER(Wait);
    UNREFERENCED_PARAMETER(WaitResult);
 
    //
    // Do something when the wait is over.
    //
    _tprintf(_T("MyWaitCallback: wait is over.\n"));
}
 
VOID DemoNewRegisterWait()
{
    PTP_WAIT Wait = NULL;
    PTP_WAIT_CALLBACK waitcallback = MyWaitCallback;
    HANDLE hEvent = NULL;
    UINT i = 0;
 
    try
    {
        //
        // Create an auto-reset event.
        //
        hEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
 
        if (NULL == hEvent) 
            throw 0;
 
        Wait = CreateThreadpoolWait(waitcallback, NULL,    NULL);
 
        if(NULL == Wait) 
        {
            _tprintf(_T("CreateThreadpoolWait failed. LastError: %u\n"), GetLastError());
            throw 1;
        }
 
        //
        // Need to re-register the event with the wait object
        // each time before signaling the event to trigger the wait callback.
        //
        for (i = 0; i < 5; i ++) 
        {
            SetThreadpoolWait(Wait, hEvent, NULL);
 
            SetEvent(hEvent);
 
            //
            // Delay for the waiter thread to act if necessary.
            //
            Sleep(500);
 
            //
            // Block here until the callback function is done executing.
            //
            WaitForThreadpoolWaitCallbacks(Wait, FALSE);
        }
    }
    catch( ... )
    {
        _tprintf( _T("Error DemoNewRegisterWait\n") );
    }
 
    if( Wait != NULL )
    {
        SetThreadpoolWait(Wait, NULL, NULL);
        CloseThreadpoolWait(Wait);
    }
 
    if( hEvent != NULL )
        CloseHandle(hEvent);
}
 
int _tmain(int argc, _TCHAR* argv[])
{
    setlocale(LC_ALL, "korean");
 
    DemoNewRegisterWait();
    DemoCleanupPersistentWorkTimer();
    
    return 0;
}


위와 같이 MS에서 제공하는 스레드풀을 간단히 몇줄 만으로 사용할 수 있다.


별도의 퍼포먼스를 요구하는 프로그램을 개발할 경우 개별로 스레드풀을 만들어 사용할 것을 권장.