ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • C++ 스레드 생성
    C++/C++ 멀티스레드 2022. 10. 5. 21:10
    728x90

    공부하며 쓰는 글입니다. 틀린게 있다면 지적해주세요

    1. 스레드 생성

     

    스레드는 특정 작업을 실행한다.

    그 작업은 함수, 람다, functor와 같은 방식으로 전달 가능하다.

     

    #include <thread>
    #include <iostream>
    
    void HelloThread()
    {
    	//이 함수가 끝나면, 이 함수를 할당받은 스레드도 종료된다.
    	cout << "Hello Thread" << endl;
    }
    
    int main()
    {
    	//스레드 생성후 스레드가 실행할 함수를 할당해준다.
    	//할당한 함수가 끝나면, 스레드도 종료된다.
    	std::thread t(HelloThread);
    }

     

    이렇게 실행하면, 에러가 발생한다.

    main() 함수도 하나의 스레드인데,

     

    t 스레드가 끝나기 전에 main스레드가 먼저 종료될 수 있다.

    따라서, 생성한 스레드에 join을 해줘야 한다.

    그러면, join한 스레드가 종료될 때까지 기다려준다.

     

    void HelloThread()
    {
    	//이 함수가 끝나면, 이 함수의 스레드도 종료됨
    	cout << "Hello Thread" << endl;
    }
    
    int main()
    {
    	//스레드 생성후 스레드가 생성할 함수를 할당해준다.
    	//할당한 함수가끝나면, 스레드도 종료된다.
    	std::thread t(HelloThread);
    	t.join();
    }

    또한 스레드 변수를 선언한다고 해서, 스레드가 생성되는 것은 아니다.

    실행할 함수를 할당해줘야 스레드가 생성된다.

    void HelloThread()
    {
    	//이 함수가 끝나면, 이 함수의 스레드도 종료됨
    	cout << "Hello Thread" << endl;
    }
    
    int main()
    {
    	//스레드 생성후 스레드가 생성할 함수를 할당해준다.
    	//할당한 함수가끝나면, 스레드도 종료된다.
    	std::thread t;
    
    	//스레드가 생성되는 시점은 함수가 할당되는 시점인 여기다.
    	t = std::thread(HelloThread);
    	
    }

     

    스레드가 실행할 함수에 인자를 넘겨줄 수도 있다.

    스레드를 생성할 때 콤마로 연결해주면 된다.

     

    void HelloThread2(int32 num)
    {
    	cout << num << endl;
    }
    
    int main()
    {
    	std::thread t(HelloThread2, 1);
    	t.join();
    }

    이렇게 하면 1이 출력되는 것을 볼 수 있다.

     

    2. 스레드 ID

    스레드는 생성되면, 고유한 ID를 갖는다.

     

    void HelloThread()
    {
    	//이 함수가 끝나면, 이 함수의 스레드도 종료됨
    	cout << "Hello Thread" << endl;
    }
    
    int main()
    {
    	//스레드 생성후 스레드가 생성할 함수를 할당해준다.
    	//할당한 함수가끝나면, 스레드도 종료된다.
    	std::thread t(HelloThread);
    	cout << t.get_id() << endl;
    	t.join();
    	cout << t.get_id() << endl;
    }

    첫 번째로 출력되는 id는 고유한 값이고,

    두 번째로 출력되는 id는 0이다.

    스레드가 실행하는 함수 HelloThread()가 종료되었기 때문이다.

     

    3. 실행중인 스레드 확인하기(joinable, detach)

    실행중인 스레드는 joinable로 확인할 수 있다.

    joinable은 단순히 get_id로 스레드 아이디를 체크해서

    0이면, false를

    0이 아니면 true를 반환한다.

     

    실행중인 스레드라면 true, 실행중이 아니라면 false인 것이다.

     

    void HelloThread()
    {
    	//이 함수가 끝나면, 이 함수의 스레드도 종료됨
    	cout << "Hello Thread" << endl;
    }
    
    int main()
    {
    	//스레드 생성후 스레드가 생성할 함수를 할당해준다.
    	//할당한 함수가끝나면, 스레드도 종료된다.
    	std::thread t;
    
    	cout << t.joinable() << endl; //0
    	t = std::thread(HelloThread);
    	cout << t.joinable() << endl; //1
    	t.join();
    	cout << t.joinable() << endl; //0
    }

     

    detach는 스레드를 종속된 스레드에서 떼어낸다고 생각하면 될것 같다.

    현재 스레드 t가 종속된 곳은 main인데, 여기서 떼어내면, 독립적으로 실행된다.

    void HelloThread()
    {
    	//이 함수가 끝나면, 이 함수의 스레드도 종료됨
    	cout << "Hello Thread" << endl;
    	std::this_thread::sleep_for(std::chrono::seconds(2));
    	cout << "Bye Thread" << endl;
    }
    
    int main()
    {
    	//스레드 생성후 스레드가 생성할 함수를 할당해준다.
    	//할당한 함수가끝나면, 스레드도 종료된다.
    	cout << "start main thread" << endl;
    	std::thread t(HelloThread);
    	t.join();
    	cout << "end main thread" << endl;	
    }

    이렇게 실행하면, main에서 t스레드가 종료될 때까지 기다린다.

    t를 main에 종속시켰기 때문이다.

     

    하지만

    void HelloThread()
    {
    	//이 함수가 끝나면, 이 함수의 스레드도 종료됨
    	cout << "Hello Thread" << endl;
    	std::this_thread::sleep_for(std::chrono::seconds(2));
    	cout << "Bye Thread" << endl;
    }
    
    int main()
    {
    	//스레드 생성후 스레드가 생성할 함수를 할당해준다.
    	//할당한 함수가끝나면, 스레드도 종료된다.
    	cout << "start main thread" << endl;
    	std::thread t(HelloThread);
    	t.detach();
    	cout << "end main thread" << endl;	
    }

    detach를 하면, 스레드 t가 실행을 끝마치지 않았더라도 main은 종료된다.

    t가 main과 독립적인 스레드가 되었기 때문이다.

     

     

    4. 병렬수행되는 스레드들

    여러개의 스레드를 생성한다면,

    코드상으로는 순차적으로 실행될 것처럼 보여도, 실제로는 그렇지 않다.

     

    void HelloThread2(int32 num)
    {
    	cout << num << endl;
    }
    
    int main()
    {
    	vector<std::thread> v;
    	
    	for (int32 i = 0; i < 10; i++)
    	{
    		v.push_back(std::thread(HelloThread2, i));
    	}
    
    	for (int32 i = 0; i < 10; i++)
    	{
    		v[i].join();
    	}
    }

    논리적으로는 0부터 9까지 차례로 출력되어야 할 것처럼 보인다

     

    실제 결과는...

    이러하다.

     

    스레드가 병렬실행되었기 때문이다.

     

    병렬실행은 속도측면에서 장점이지만,

    데이터를 읽고쓰거나 할 때는 덮어쓰기 문제가 발생하기도 한다.

    따라서 나중에 배울 동기화 기법을 적용해서 문제를 해결할 수 있다.

     

    ps. join()함수

    void HelloThread2(int32 num)
    {
    	cout << num << endl;
    }
    
    int main()
    {
    	vector<std::thread> v;
    	
    	for (int32 i = 0; i < 10; i++)
    	{
    		v.push_back(std::thread(HelloThread2, i));
    		v[i].join();
    
    	}
    }

    이렇게 하니 출력이 잘 된다. 신기하다.

     

    cppreference에 따르면 join함수는 이렇게 설명되어 있다.

    Blocks the current thread until the thread identified by *this finishes its execution

    join이 걸린 시점부터 join을 호출한(예제에서는 main)스레드가 실행을 멈춘다는 것 같다.

    따라서 thread(HelloThread2, 0)이 다 끝난 뒤에 다시 main스레드가 실행되어서 다음 iteration으로 넘어가는 것 같다.

    728x90

    'C++ > C++ 멀티스레드' 카테고리의 다른 글

    C++ SpinLock구현  (0) 2022.10.09
    멀티스레드 Lock구현  (0) 2022.10.09
    C++, 멀티스레드 교착상태(DeadLock)  (0) 2022.10.08
    C++ 멀티스레드, lock(mutex)  (1) 2022.10.06
    C++ 멀티스레드, Atomic  (0) 2022.10.05

    댓글

Designed by Tistory.