[C++과 언리얼로 만드는 MMORPG 게임 개발 시리즈] Part4: 게임 서버 - 멀티쓰레드 개론, 쓰레드 생성

Jangmanbo·2023년 6월 17일
0

이론

CPU

여러 프로세스(메모리에 적재되어 실제로 실행 중인 프로그램)를 CPU 코어가 연산을 수행
이 프로세스들을 짧은 시간 안에 왔다갔다하며 수행하기 때문에 마치 동시에 연산을 수행하는 것처럼 보임


멀티 스레드

  • 일반적으로 하나의 프로세스는 하나의 스레드를 가짐
  • 스레드가 여러 개인 이유는 여러 가지 일을 분배하여 한 번에 동시다발적으로 처리하기 위함
  • 동일한 프로세스에 존재하는 스레드의 경우 프로세스의 Code, Heap(동적 할당), Data(전역, 정적 변수)영역을 공유

System call

  • C++의 cout과 같은 시스템콜은 프로그램이 운영체제의 커널에게 요청하는 작업 (프로그램이 직접 출력할 수 없고 운영체제가 대신 함)

  • 윈도우, 리눅스 등 다양한 환경에서 동작할 수 있는 system call 함수를 사용하자 (윈도우 한정인 system call을 사용한다면 리눅스에서는 구동되지 않음)



실습

#include "pch.h"
#include <iostream>
#include "CorePch.h"

#include <thread>

void HelloThread()
{
    cout << "Hello Thread" << endl;
}

int main()
{
    std::thread t(HelloThread);	// HelloThread를 실행하는 t라는 이름의 스레드 생성

    cout << "Hello Main" << endl;
}

main 함수에서 단순히 HelloThread를 호출한다면 그대로 메인 스레드에서 실행되지만, 스레드 t를 생성하면 메인스레드와 스레드t가 병렬적으로 실행된다.

스레드 t를 메인스레드가 관리하고 있는데 메인스레드가 먼저 종료되면 에러가 발생한다.

int main()
{
    std::thread t(HelloThread);	// HelloThread를 실행하는 t라는 이름의 스레드 생성

    cout << "Hello Main" << endl;
    
    t.join();	// t가 끝날 때까지 기다림 (t는 HelloThread를 모두 실행하면 종료)
}


이렇게 스레드 t가 종료될 때까지 기다리면 실행시 에러가 발생하지 않는 것을 볼 수 있다.




thread 클래스 함수

hardware_concurrency() : CPU 코어 개수, 즉 실행할 수 있는 프로세스 개수
get_id() : 스레드의 고유 id
detach() : 스레드와의 연결을 끊음. 즉 std::thread객체에서 실제 스레드를 분리
joinable() : 실제로 스레드가 존재하는지 핀딘
join() : 스레드가 끝날 때까지 기다림

예시

int main()
{
    std::thread t;  // 스레드 변수만 생성

    auto id1 = t.get_id(); // 스레드 변수만 존재하고 실제로 스레드가 존재하지 않기 때문에 0을 리턴

    t = std::thread(HelloThread);   // 스레드 생성

    auto id2= t.get_id(); // 유효한 id 리턴
    int32 count = t.hardware_concurrency();// CPU 코어 개수, 즉 실행할 수 있는 프로세스 개수

    if (t.joinable())
    {
        t.join();
    }

    cout << "Hello Main" << endl;
}



스레드 생성 시 함수의 인자를 함께 넘길 수 있다.

void HelloThread_2(int32 num)
{
    cout << num << endl;
}

int main()
{
    vector<std::thread> v;

    for (int32 i = 0; i < 10; i++)
    {
        v.push_back(std::thread(HelloThread_2, i));
    }

    for (int32 i = 0; i < 10; i++)
    {
        if (v[i].joinable())
        {
            v[i].join();
        }
    }

    cout << "Hello Main" << endl;
}

실행 결과

0, 1, 2, 3, 4, ... 순서대로 숫자가 호출되지 않는 이유는 스레드 생성의 순서는 정해져 있지만 생성된 스레드들은 병렬적으로 동작하기 때문이다.

0개의 댓글