OS&네트워크

운영체제 #2_ C언어를 통한 스레드/멀티스레드 예제

Tigercow.Door 2017. 10. 26. 21:24



안녕하세요. 문범우입니다.

지난 포스팅에서 프로세스 및 스레드에 대한 개념을 정리해보았습니다.

이번에는 스레드를 직접적으로 만들어서 확인해보도록 하겠습니다.

실습은 C언어로 진행합니다.


1. 싱글 스레드(Single-Thread)

먼저 싱글 스레드를 만들어서 확인해보도록 하죠.

코드는 아래와 같습니다.

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
#define WIN32_LEAN_AND_MEAN
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
 
DWORD WINAPI ThreadFunc(LPVOID);
// DWORD는 더블워드 = long
// WINAPI : Windows의 API
// LPVOID : ms에서 사용하는 void* 
 
int global = 1;
 
int main()
{
    HANDLE hThrd; //스레드를 접근하기 위한 손잡이 개념
    DWORD threadId; 
    int i;
    // for 루프문
    for (i=0; i<5; i++)
    {
        ThreadFunc( (LPVOID) i); // single thread
    }
    // Wait for the threads to complete.
    Sleep(3000);
 
    return EXIT_SUCCESS;
}
 
DWORD WINAPI ThreadFunc(LPVOID n)
{
    int i;
    for (i=0;i<100;i++){
        printf("%d%d%d%d%d%d%d%d global = %d\n",n,n,n,n,n,n,n,n,global++);
    }
    return 0;
}
cs


11번 라인은 추후 멀티 스레드에서 확인하기 위한 변수입니다.

21번 라인을 보시면 ThreadFunc 함수를 호출 하도록 되어 있습니다.

그리고 해당 함수에 대해서는 코드 하단에 나와 있습니다.

24번 라인은 결과를 끝까지 확인하기 위해 일시적으로 추가한 코드입니다.


해당 코드에서는 스레드를 추가적으로 만들지 않습니다.

따라서 프로세스에는 Primary thread 하나만 존재함으로써 단일 스레드를 유지합니다.

코드에 대한 결과는 어떻게 될까요?

싱글스레드 실행결과 중간


싱글스레드 실행결과 끝


위의 실행결과를 확인하면 코드에서 for문에 의해서 ThreadFunc 함수가 5번 호출되는 것을 알 수 있습니다.

처음에 ThreadFunc함수가 호출되고 100번 반복될때 까지 다음 코드를 실행하지 않고 함수가 리턴될때 까지 기다립니다.

이것은 출력되는 결과를 보면 알 수 있죠.

함수가 for문의 i를 인자로 받아서 이를 출력하기 때문에 몇번째로 호출된 함수인지를 확인할 수 있습니다.


2. 멀티 스레드(Multi-Thread)

그럼 해당 코드를 약간 바꿔서 단일스레드가 아닌 멀티스레드로 확인해보겠습니다.


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
#define WIN32_LEAN_AND_MEAN
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
 
DWORD WINAPI ThreadFunc(LPVOID);
// DWORD는 더블워드 = long
// WINAPI : Windows의 API
// LPVOID : ms에서 사용하는 void* 
 
int global = 1;
 
int main()
{
    HANDLE hThrd; //스레드를 접근하기 위한 손잡이 개념
    DWORD threadId; 
    int i;
    // for 루프문
    for (i=0; i<5; i++)
    {
        //ThreadFunc( (LPVOID) i); // single thread 
        //CreateThread함수를 5번 호출
        hThrd = CreateThread(NULL,
            0
            ThreadFunc, //함수포인터: 함수가 시작하는 메모리 주소
                        //아래에서 함수 확인!
            (LPVOID)i, //
            0,
            &threadId ); //
        if (hThrd)
        {
            printf("Thread launched %d\n", i);
            CloseHandle(hThrd);
        }
    }
    // Wait for the threads to complete.
    Sleep(1000);
 
    return EXIT_SUCCESS;
}
 
DWORD WINAPI ThreadFunc(LPVOID n)
{
    int i;
    for (i=0;i<100;i++){
        printf("%d%d%d%d%d%d%d%d global = %d\n",n,n,n,n,n,n,n,n,global++);
    }
    return 0;
}
 
cs


해당 코드를 보면 단순히 ThreadFunc 함수를 호출하는 것이 아니고,

CreateThread 함수를 통해 스레드를 만들고 해당 스레드가 ThreadFunc 함수를 호출하게 합니다.

그럼 그 결과가 어떻게 바뀔까요?


멀티스레드 실행결과 중간


멀티스레드 실행결과 끝


좀 전에 확인한 싱글스레드와의 차이가 보이시나요?

각각의 스레드가 각자 실행되면서 global 변수의 값을 증가시키고 있습니다.


3. 싱글 스레드와 멀티 스레드 시간 차이

이러한 차이가 어떤 결과를 보여줄까요?

각각의 코드에 시간측정함수를 더해 확인해보도록 하겠습니다.

먼저 싱글 스레드의 코드입니다.


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
#define WIN32_LEAN_AND_MEAN
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <windows.h>
#define MAXTHREAD 5
 
DWORD WINAPI ThreadFunc(LPVOID n);
int main () {
    clock_t start, finish;
    double duration;
    DWORD threadId; 
    HANDLE hThrd[MAXTHREAD];
 
    int i;
    start = clock();//현재 시간 반환
 
    for (i=0; i<MAXTHREAD; i++){
        ThreadFunc((LPVOID)i);
    }
    WaitForMultipleObjects(MAXTHREAD, hThrd, TRUE, INFINITE);
    for (i=0; i<MAXTHREAD; i++){
        CloseHandle(hThrd[i]);
    }
    //종료
    finish = clock();
    duration = (double) (finish-start) / CLOCKS_PER_SEC;
    printf("%f 초입니다\n", duration);
    return 0;
}
DWORD WINAPI ThreadFunc(LPVOID n)
{
    for (int i = 0; i<1000000000; i++){
        3+5+8*24;
    }
    return 0;
}
cs


위의 코드를 보면 18번 for문을 통해 ThreadFunc 함수가 총 5번 실행됩니다.

ThreadFunc에서는 단일 연산을 1억번 수행하게됩니다.

그러므로, 해당 싱글스레드에서는 단일 연산을 총 5억번 수행합니다.

21번 라인은 멀티스레드가 종료할때까지 기다려라 라는 함수인데 이에 대한 것은 추후에 설명드리도록 하겠습니다.

해당 코드에 대한 결과는 아래와 같습니다.



대략적으로 12초가 걸렸습니다.

그럼 멀티스레드에서는 어떨까요? 먼저 코드를 확인해보겠습니다.


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
#define WIN32_LEAN_AND_MEAN
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <windows.h>
#define MAXTHREAD 5
 
DWORD WINAPI ThreadFunc(LPVOID n);
int main () {
    clock_t start, finish;
    double duration;
    DWORD threadId; 
    HANDLE hThrd[MAXTHREAD];
 
    int i;
    start = clock();//현재 시간 반환
 
    for (i=0; i<MAXTHREAD; i++){
        hThrd[i] = CreateThread(NULL
            0,        
            ThreadFunc, 
            (LPVOID)i, 
            0,            
            &threadId    
            ); 
    }
    WaitForMultipleObjects(MAXTHREAD, hThrd, TRUE, INFINITE);
    for (i=0; i<MAXTHREAD; i++){
        CloseHandle(hThrd[i]);
    }
    //종료
    finish = clock();
    duration = (double) (finish-start) / CLOCKS_PER_SEC;
    printf("%f 초입니다\n", duration);
    return 0;
}
DWORD WINAPI ThreadFunc(LPVOID n)
{
    for (int i = 0; i<1000000000; i++){
        3+5+8*24;
    }
    return 0;
}
cs


19번~25번 라인을 통해 각각 스레드를 만듭니다.

각각의 스레드가 1억번 단일연산을 수행하므로 위와 동일하게 총 5억번의 연산을 수행합니다.

이러한 멀티스레딩에서의 시간 결과는 어떻게 나올까요?



싱글스레드에서는 약 12초가 소요되었던 5억번의 단일연산이 멀티스레드에서는 3.6초로 줄어들었습니다.

물론 개인 컴퓨터 사양에 따라 시간은 변동될 수 있지만, 그런 점들을 감안해도

싱글스레드와 멀티스레드에 대한 시간효율 차이는 너무나 명백하게 확인할 수 있습니다.




728x90