728x90

이제 thread의 개념을 프로그래밍에 대입해 볼 것이다.

 

이전 CUDA를 활용해 GPU 환경에서 멀티스레드를 활용해 행렬계산을 했던 것과 비슷한 방식으로 CPU에서도 스레드를 생성하여 동시 작업이 가능하다.

 

이때, 중요한 부분은 데이터 신뢰성을 위해 모든 작업이 끝난 이후 데이터에 접근을 해야한다는 것이다.

 

이를 위해 적용 가능한 방식은 총 3가지가 존재한다.

1. Sleep(int time)

임의의 시간을 설정하여 time ms 코드 진행을 중지시키는 함수이다.

하지만 이 방식은 모든 컴퓨터 환경의 처리속도가 다르고, 의미없이 프로그램 수행 속도를 늦추는 원인이 될 수 있다.

 

2. WaitForSingleObject(HANDLE hHandle, DWORD dwMillseconds)

이 방법은 hHandle이 signaled이 되는 순간까지 기다리는 명령을 지시하는 방법이다. signaled가 된 경우라면 "WAIT_OBJECT_0" 를 리턴하며 함수를 종료한다.

이때, thread가 무한히 종료되지 않는 문제를 피하기 위해 dwMillseconds를 이용해 정해진 시간을 초과하는 경우 "WAIT_FAILED" 를 리턴하며 함수를 종료한다.

 

3. WaitForMultipleObjects

DWORD WaitForMultipleObjects( DWORD nCount, const HANDLE *lpHandles, BOOL bWaitAll, DWORD dwMilliseconds );

이제 2번의 함수의 확장이다. nCount개의 threads가 전부 signal 되는 순간까지 기다리는 함수이다.

위와 리턴하는 것은 동일하므로 거의 문제가 되지 않지만, 이제 최대 기다릴 수 있는 threads 수를 알아두어야 한다.

최대 nCount 수는 MAXIMUM_WAIT_OBJECTS를 넘으면 함수가 정상적으로 동작하지 않는다. 이때 MAXIMUM_WAIT_OBJECTS=64 로 최대 64개의 thread를 검사할 수 있다.

 

- 소스코드

 

1.

/*
 * Numbers.c
 *
 * Sample code for "Multithreading Applications in Win32"
 * This is from Chapter 2, Listing 2-1
 *
 * Starts five threads and gives visible feedback
 * of these threads running by printing a number
 * passed in from the primary thread.
 *
 */

#define WIN32_LEAN_AND_MEAN         //필요없는 헤더들을 방지하고, 데이터의 중복정의를 제거하여
                                    //빌드시간을 단축한다.
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>                //Windows API를 위해 필요

DWORD WINAPI ThreadFunc(LPVOID);    //DWORD <==unsinged long







int main()
{
    HANDLE hThrd;       //모든 커널 오브젝트는 핸들을 통해서만 접근할 수 있다.
    DWORD threadId;
    int i;

    for (i = 0; i < 5; i++)
    {
        //CreateThread()함수는
        //1. 쓰레드를 생성하여 실행하고,
        //2. 쓰레드 커널 오브젝트를 생성하여 그 핸들을 반환한다.
        hThrd = CreateThread(NULL,      //보안관련
            0,                          //스택 크기
            ThreadFunc,                 //함수 포인터
            (LPVOID)i,                  //매개 변수 포인터
            0,
            &threadId);
        if (hThrd)                      //hThrd가 영이 아니면, 즉 CreateThread()가 오류를 발생하지 않으면
        {
            printf("Thread launched %d\n", i);
            CloseHandle(hThrd);
        }
        //ThreadFunc((LPVOID)i);
    }
    // Wait for the threads to complete.
    // We'll see a better way of doing this later.
    Sleep(2000);            //2000ms동안 중지한다.
                            //하지만 이런 접근은 모든 컴퓨터 환경에서 thread를 끝낸다는 것을 보장하지 못한다.

    //따라서 다음과 같이 활용하게 된다.

    //모든 커널 오브젝트는 상태(깃발: state)를 가진다.
    //깃발이 올라간 상태(signaled state)와 깃발이 내려간 상태 (non-signaled state)
    //커널 오브젝트의 상태는 각 오브젝트마다 의미를 가진다.
    //쓰레드 커널 오브젝트는
    //1. signaled state의 의미 : 쓰레드 종료
    //2. non-signaled state의 의미: 쓰레드 실행 중



    return EXIT_SUCCESS;
}

DWORD WINAPI ThreadFunc(LPVOID n)
{
    int i;
    for (i = 0; i < 10; i++)
        printf("%d%d%d%d%d%d%d%d\n", n, n, n, n, n, n, n, n);
    return 0;
}

 

2.

/*
 * Numbers.c
 *
 * Sample code for "Multithreading Applications in Win32"
 * This is from Chapter 2, Listing 2-1
 *
 * Starts five threads and gives visible feedback
 * of these threads running by printing a number
 * passed in from the primary thread.
 *
 */

#define WIN32_LEAN_AND_MEAN         //필요없는 헤더들을 방지하고, 데이터의 중복정의를 제거하여
 //빌드시간을 단축한다.
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>                //Windows API를 위해 필요

DWORD WINAPI ThreadFunc(LPVOID);    //DWORD <==unsinged long







int main()
{
    HANDLE hThrd;       //모든 커널 오브젝트는 핸들을 통해서만 접근할 수 있다.
    DWORD threadId;
    int i = 0;
    hThrd = CreateThread(NULL,      //보안관련
        0,                          //스택 크기
        ThreadFunc,                 //함수 포인터
        (LPVOID)i,                  //매개 변수 포인터
        0,
        &threadId);


    //Sleep(20);            //2000ms동안 중지한다.
                            //하지만 이런 접근은 모든 컴퓨터 환경에서 thread를 끝낸다는 것을 보장하지 못한다.

    //따라서 다음과 같이 활용하게 된다.

    //모든 커널 오브젝트는 상태(깃발: state)를 가진다.
    //깃발이 올라간 상태(signaled state)와 깃발이 내려간 상태 (non-signaled state)
    //커널 오브젝트의 상태는 각 오브젝트마다 의미를 가진다.
    //쓰레드 커널 오브젝트는
    //1. signaled state의 의미 : 쓰레드 종료
    //2. non-signaled state의 의미: 쓰레드 실행 중


    //쓰레드가 종료될 때(즉, hThrd가 signaled state)까지 기다린다.
    ////이때 우리는 끝날때까지 무한하게 기다린다.
    ////하지만 이런 경우에는 오류에도 무한하게 기다리게 된다.
    //WaitForSingleObject(hThrd, INFINITE);
    DWORD ret = WaitForSingleObject(hThrd, 10);       //2000ms만 기다리게 된다.
    if (ret == WAIT_OBJECT_0) {
        printf("the kernel object is signaled\n");
    }
    else if (ret == WAIT_TIMEOUT) {
        printf("the kernel object is timeout\n");
    }

    if (hThrd)                      //hThrd가 영이 아니면, 즉 CreateThread()가 오류를 발생하지 않으면
    {
        printf("Thread launched %d\n", i);
        CloseHandle(hThrd);
    }
    else {
        printf("Error creating thread\n");
        return EXIT_FAILURE;
    }


    return EXIT_SUCCESS;
}

DWORD WINAPI ThreadFunc(LPVOID n)
{
    int i;
    for (i = 0; i < 100; i++)
        printf("i=%d,  %d%d%d%d%d%d%d%d\n", i, n, n, n, n, n, n, n, n);
    return 0;
}

 

3.

// 20201104_thread4.cpp : 이 파일에는 'main' 함수가 포함됩니다. 거기서 프로그램 실행이 시작되고 종료됩니다.
//

/*
 * Numbers.c
 *
 * Sample code for "Multithreading Applications in Win32"
 * This is from Chapter 2, Listing 2-1
 *
 * Starts five threads and gives visible feedback
 * of these threads running by printing a number
 * passed in from the primary thread.
 *
 */

#define WIN32_LEAN_AND_MEAN         //필요없는 헤더들을 방지하고, 데이터의 중복정의를 제거하여
 //빌드시간을 단축한다.
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>                //Windows API를 위해 필요

DWORD WINAPI ThreadFunc(LPVOID);    //DWORD <==unsinged long







int main()
{
    HANDLE hThrd[5];       //모든 커널 오브젝트는 핸들을 통해서만 접근할 수 있다.
    DWORD threadId, ret;
    int i = 0;
    for (i = 0; i < 5; i++) {
        hThrd[i] = CreateThread(NULL,      //보안관련
            0,                          //스택 크기
            ThreadFunc,                 //함수 포인터
            (LPVOID)i,                  //매개 변수 포인터
            0,
            &threadId);


        if (hThrd[i]) {
            printf("Thread launched %d\n", i);
        }
        else {
            printf("Error creating thread\n");
            return EXIT_FAILURE;
        }
    }
    

    ////쓰레드가 종료될 때(즉, hThrd가 signaled state)까지 기다린다.
    //////이때 우리는 끝날때까지 무한하게 기다린다.
    //////하지만 이런 경우에는 오류에도 무한하게 기다리게 된다.
    ////WaitForSingleObject(hThrd, INFINITE);
    //for (i = 0; i < 5; i++) {
    //    ret = WaitForSingleObject(hThrd[i], 1000);       //2000ms만 기다리게 된다.
    //    if (ret == WAIT_OBJECT_0) {
    //        printf("hThrd[%d] : the kernel object is signaled\n", i);
    //    }
    //    else if (ret == WAIT_TIMEOUT) {
    //        printf("hThrd[%d] : the timeout interval elapsed, and the object's state is nonsignaled\n", i);
    //    }
    //}
    
    //위의 방법으로 기다리는 시간이 정확하지 않다.
    //위의 방법은 사용하지 않는다.
    ret = WaitForMultipleObjects(5, hThrd, TRUE, INFINITE);   //5개 thread 상태를 동시에 기다린다.
                                                              //false는 1개만 통과해도 지나간다.
    if (ret == WAIT_OBJECT_0) {
        printf("hThrd : the kernel object is signaled\n");
    }
    else if (ret == WAIT_TIMEOUT) {
        printf("hThrd : the timeout interval elapsed, and the object's state is nonsignaled\n");
    }

    printf("the thread is terminated\n");

    //closehandle은 다음시간에
    CloseHandle(hThrd);



    return EXIT_SUCCESS;
}

DWORD WINAPI ThreadFunc(LPVOID n)
{
    int i;
    for (i = 0; i < 100; i++)
        printf("i=%d,  %d%d%d%d%d%d%d%d\n", i, n, n, n, n, n, n, n, n);
    return 0;
}

 

 

 

docs.microsoft.com/ko-kr/windows/win32/api/synchapi/nf-synchapi-waitforsingleobject?f1url=%3FappId%3DDev16IDEF1%26l%3DKO-KR%26k%3Dk(SYNCHAPI%252FWaitForSingleObject);k(WaitForSingleObject);k(DevLang-C%252B%252B);k(TargetOS-Windows)%26rd%3Dtrue

docs.microsoft.com/ko-kr/windows/win32/api/synchapi/nf-synchapi-waitformultipleobjects?f1url=%3FappId%3DDev16IDEF1%26l%3DKO-KR%26k%3Dk(SYNCHAPI%252FWaitForMultipleObjects);k(WaitForMultipleObjects);k(DevLang-C%252B%252B);k(TargetOS-Windows)%26rd%3Dtrue

+ Recent posts