본문 바로가기
dev/c++

[c++] 쓰레드 생성 (pthread)

by 최연탄 2023. 6. 2.
728x90
반응형

참고: https://www.tutorialspoint.com/cplusplus/cpp_multithreading.htm

멀티쓰레딩은 멀티태스킹에 특화된 형태이며 멀티태스킹은 컴퓨터에서 두 개 이상의 프로그램을 동시에 실행하는 기능을 말합니다. 일반적으로 프로세스 기반과 쓰레드 기반의 두 가지 멀티태스킹 유형이 있습니다.

프로세스 기반 멀티태스킹은 여러 프로그램의 동시 실행을 처리합니다. 쓰레드 기반 멀티태스킹은 동일한 프로그램 내 여러 조각의 동시 실행을 처리합니다.

멀티쓰레드 프로그램에는 동시에 실행할 수 있는 두 개 이상의 부분을 가지고 있습니다. 이러한 프로그램의 각 부분을 쓰레드라고 하며 각 쓰레드는 별도의 실행 경로를 가집니다.

C++ 11 이전에는 멀티쓰레드 응용프로그램에 대한 기본 지원이 없었습니다. 대신 이 기능을 운영체제에 전적으로 의존했습니다.

이 포스트에서는 리눅스 운영체제에서 POSIX를 사용하여 멀티쓰레드 C++ 프로그램을 작성하겠습니다. POSIX thread 또는 pthread는 FreeBSD, NetBSD, GNU/Linux, macOS, Solaris와 같은 많은 Unix 계열 POSIX 시스템에서 사용할 수 있는 API를 제공합니다.

쓰레드 생성

다음은 POSIX thread를 생성하는 구문입니다:

#include <pthread.h>

pthread_create(thread, attr, start_routine, arg);

여기서 pthread_create()는 새로운 쓰레드를 생성하고 실행할 수 있게 만듭니다. 이 루틴은 코드 내 어디서나 여러 번 호출할 수 있습니다. 매개변수에 대한 설명은 다음과 같습니다:

  • thread: 서브루틴에서 리턴한 새 쓰레드의 고유 식별자입니다.
  • attr: 쓰레드 속성을 설정하는 속성 객체입니다. 쓰레드 속성 객체를 전달하거나 기본 값인 NULL을 전달할 수 있습니다.
  • start_routine: 쓰레드가 생성되면 쓰레드가 실행할 C++ 루틴입니다.
  • arg: start_routine에 전달할 인수입니다. void 형식의 포인터 참조로 전달해야합니다. 전달할 인수가 없으면 NULL을 사용할 수 있습니다.

프로세스에 의해 생성할 수 있는 최대 쓰레드 수는 구현에 따라 다릅니다. 생성된 쓰레드들은 동등한 레벨을 가지며 또 다른 쓰레드를 생성할 수도 있습니다. 쓰레드 간에는 암묵적인 계층 구조나 종속성이 없습니다.

쓰레드 종료

다음은 POSIX thread의 종료 구문입니다:

#include <pthread.h>

pthread_exit(status);

여기서 pthread_exit()는 스레드를 명시적으로 종료합니다. 일반적으로 pthread_exit() 루틴은 쓰레드가 작업을 완료하고 더 이상 존재할 필요가 없는 경우 호출합니다.

한 쓰레드가 main()이 생성한 쓰레드 보다 먼저 완료되고 pthread_exit()로 종료되더라도 다른 쓰레드는 계속 실행됩니다. 종료되지 않고 계속 실행 중인 쓰레드들은 main()이 완료되면 자동으로 종료됩니다.

기본 예제

다음의 간단한 예제 코드는 pthread_create() 루틴으로 5 개의 쓰레드를 생성합니다. 각 쓰레드는 "Hello World!"를 출력하고  pthread_exit()를 호출하여 쓰레드를 종료합니다.

#include <iostream>
#include <cstdlib>
#include <pthread.h>

using namespace std;

#define NUM_THREADS 5

void * printHello(void *threadid) {
    long tid = (long)threadid;

    cout << "Hello World! Thread ID: " << tid << endl;

    pthread_exit(NULL);
}

int main () {
    pthread_t threads[NUM_THREADS];
    int rc;
    int i;

    for (i = 0; i < NUM_THREADS; i++) {
        cout << "main() : creating thread, " << i << endl;

        rc = pthread_create(&threads[i], NULL, printHello, (void *)i);

        if (rc) {
            cout << "Error: unable to create thread: " << rc << endl;
            exit(-1);
        }
    }

    pthread_exit(NULL);
}

쓰레드에 인수 전달

이 예제는 구조체를 통해 여러 인수를 전달하는 방법을 보여줍니다. 예제에서 보는 것 처럼 arg는 void 형 포인터이기 때문에 쓰레드 콜백에 모든 데이터 유형을 전달할 수 있습니다.

#include <iostream>
#include <cstdlib>
#include <pthread.h>

using namespace std;

#define NUM_THREADS 5

struct thread_data {
    int thread_id;
    const char * message;
};

void * printHello(void * threadarg) {
    struct thread_data * my_data = (struct thread_data *) threadarg;

    cout << "Thread ID : " << my_data->thread_id;
    cout << " Message : " << my_data->message << endl;

    pthread_exit(NULL);
}

int main () {
    pthread_t threads[NUM_THREADS];
    struct thread_data td[NUM_THREADS];
    int rc;
    int i;

    for (i = 0; i < NUM_THREADS; i++) {
        cout << "main() : creating thread, " << i << endl;

        td[i].thread_id = i;
        td[i].message = "This is message";

        rc = pthread_create(&threads[i], NULL, printHello, (void *)&td[i]);

        if (rc) {
            cout << "Error:unable to create thread," << rc << endl;
            exit(-1);
        }
    }

    pthread_exit(NULL);
}

쓰레드 기다리기

다음 두 개의 루틴은 쓰레드를 기다리거나 독립적으로 자원을 할당 합니다:

pthread_join(threadid, status);
pthread_detach(threadid);

pthread_join() 서브루틴은 지정된 "threadid" 쓰레드가 종료될 때 까지 호출 쓰레드를 정지 시킵니다. 쓰레드를 생성할 때 속성에 따라 기다리게할 수 있는 쓰레드 인지 독립적인 자원을 가질 수 있는 쓰레드인지를 정의합니다. joinable 속성으로 생성된 쓰레드만 기다릴 수 있습니다. 쓰레드가 detached 상태로 생성되면 절대 기다릴 수 없습니다.

다음의 예제는 pthread_join() 루틴을 사용하여 쓰레드 완료를 기다리는 방법을 보입니다:

#include <iostream>
#include <cstdlib>
#include <pthread.h>
#include <unistd.h>

using namespace std;

#define NUM_THREADS 5

void * wait(void * t) {
    long tid = (long)t;

    sleep(1);

    cout << "Sleeping in thread " << endl;
    cout << "Thread with id : " << tid << "  ...exiting " << endl;

    pthread_exit(NULL);
}

int main () {
    int rc;
    int i;
    pthread_t threads[NUM_THREADS];
    pthread_attr_t attr;
    void * status;

    pthread_attr_init(&attr);
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);

    for (i = 0; i < NUM_THREADS; i++) {
        cout << "main() : creating thread, " << i << endl;

        rc = pthread_create(&threads[i], &attr, wait, (void *)i);

        if (rc) {
            cout << "Error:unable to create thread," << rc << endl;
            exit(-1);
        }
    }

    pthread_attr_destroy(&attr);

    for (i = 0; i < NUM_THREADS; i++) {
        rc = pthread_join(threads[i], &status);

        if (rc) {
            cout << "Error:unable to join," << rc << endl;
            exit(-1);
        }

        cout << "Main: completed thread id :" << i ;
        cout << "  exiting with status :" << status << endl;
    }

    cout << "Main: program exiting." << endl;

    pthread_exit(NULL);
}

반응형

댓글