SW 그리고 아빠 엔지니어링 중...

아는 만큼 보이고, 경험해봐야 알 수 있고, 자꾸 써야 내 것이 된다.

L C++/Concurrency

[Concurrency] 멀티(복수) 스레드를 보관하는 방법

보리남편 김 주부 2023. 8. 15. 08:00
728x90
멀티 스레드를 관리하기 위해 vector를 이용하려고 해도 스레드는 복사생성자를 지원하지 않아 일반 데이터와 동일하게 처리를 할 수 없다.

 

vector를 이용해 멀티스레드를 보관하려는 아래 예제를 보자.

#include <vector>
#include <thread>
#include <algorithm>
#include <functional>

void do_work(unsigned id)
{}

int main()
{
    // 참고..
    std::thread t1(&do_work, 1);
    v1.push_back(t1);   // error.  복사 생성자가 필요한데..
                            // std::thread 는 복사 될수 없는타입

    // create.. 10 thread
    std::vector<std::thread> v1;  // 초기 크기 0

    for (int i = 0; i < 10; i++)
    {
        // 참고..
      std::thread t1(&do_work, 1);
      v1.push_back(t1);   // error.  복사 생성자가 필요한데..
                            // std::thread 는 복사 될수 없는타입
    }
}

vector에 스레드를 보관하려고 하면 push_back 하는 시점에 복사 생성자가 필요한데 thread가 지원하지 않기에 vector를 사용할 수가 없다.

 

임시객체를 이용하기


push_back에 넣을 때 임시객체로 생성하면 vector에 넣는 게 가능하다.

#include <vector>
#include <thread>
#include <algorithm>
#include <functional>

void do_work(unsigned id)
{}

int main()
{
    // 2. create.. 10 thread
    std::vector<std::thread> v1;  // 초기 크기 0

    for (int i = 0; i < 10; i++)
    {
        // 임시객체 형태로 넣기
         v1.push_back( std::thread(&do_work, 1) );
    }

  // 모두 join
  for (auto& t : v1)
      t.join();

}
std::thread(&do_work, 1) 이 코드는 임시 객체로 생성(rvalue)된 것이기에 복사가 아니라 move로 대입된 것이기에 넣는 게 가능하다.

 

emplace_back 이용하기


emplace_back 함수 원형을 먼저 확인해 보자.

template< class... Args >
void emplace_back( Args&&... args );
  (since C++11)
(until C++17)

emplace_back는 push_back과 달리 삽입 객체를 받는 게 아니고 삽입할 객체의 생성자를 위한 인자를 받는다. 그리고 std:vector 내에서 직접 객체를 생성하여 삽입한다.

#include <vector>
#include <thread>
#include <algorithm>
#include <functional>

void do_work(unsigned id)
{}

int main()
{

    // 2. create.. 10 thread
    std::vector<std::thread> v1;

    for (int i = 0; i < 10; i++)
    {
      //std::thread t1(&do_work, 1);
      //v1.push_back(t1);

      // push_back 대신에 emplace_back 으로 넣기
      // => push_back    : 대상타입의 객체를 만들어서 전달
      // => emplace_back : 대상타입을 만들기 위한 생성자 인자전달
      v1.emplace_back(&do_work, 1);
    }

    // 모두 join
  for (auto& t : v1)
      t.join();
}
v1.emplace_back(&do_work, 1) 를 보면
v1는 std::thread 타입의 vector 이기에 thread의 인자만 전달하면, vector 내에서 thread 객체를 생성하여 삽입된다.

 

참고


for문 대신에 for_each를 쓸 수도 있다.

#include <vector>
#include <thread>
#include <algorithm>
#include <functional>

void do_work(unsigned id)
{}

int main()
{
    // 2. create.. 10 thread
    std::vector<std::thread> v1;

    for (int i = 0; i < 10; i++)
    {
        v1.push_back( std::thread(&do_work, 1) );
    }

    // 모두 join
//    for (auto& t : v1)
//      t.join();

    std::for_each(v1.begin(), v1.end(),
                        [](auto& t) { t.join(); } );

}

 

참고


* 본 글에 예제 코드는 코드누리 교육을 받으면서 제공된 샘플코드를 활용하였습니다.

728x90
728x90