멤버 함수로 스레드 시작


294

std::thread인수를 사용하지 않고을 반환하는 멤버 함수 로을 구성하려고 합니다 void. 작동하는 구문을 알 수 없습니다. 컴파일러는 무엇이든 불평합니다. 실행 spawn()하는 a std::thread를 반환 하도록 구현하는 올바른 방법은 무엇입니까 test()?

#include <thread>
class blub {
  void test() {
  }
public:
  std::thread spawn() {
    return { test };
  }
};

1
함수가 void라는 void를 반환하거나 매개 변수가 없음을 의미합니까? 당신이하려는 일에 대한 코드를 추가 할 수 있습니까?
Zaid Amir

테스트 했습니까? (아직하지 않았습니다.) 귀하의 코드는 RVO (return-value-optimzation)에 의존하는 것 같지만 그렇게 생각하지는 않습니다. 복사 생성자가 없기 때문에 사용하는 std::move( std::thread(func) );것이 더 좋습니다 std::thread.
RnMss

4
@RnMss : 이 경우에는 중복을 사용하여 RVO에 의존 할 수 있습니다 . std::move이것은 사실이 아니며, 복사 생성자가 없었으므로 컴파일러는 오류를 발생시킵니다.
Qualia

답변:


367
#include <thread>
#include <iostream>

class bar {
public:
  void foo() {
    std::cout << "hello from member function" << std::endl;
  }
};

int main()
{
  std::thread t(&bar::foo, bar());
  t.join();
}

편집 : 편집을 회계 처리하려면 다음과 같이해야합니다.

  std::thread spawn() {
    return std::thread(&blub::test, this);
  }

업데이트 : 더 많은 요점을 설명하고 싶습니다. 일부 의견도 설명했습니다.

위에서 설명한 구문은 INVOKE 정의 (§20.8.2.1)에 따라 정의됩니다.

INVOKE (f, t1, t2, ..., tN)를 다음과 같이 정의하십시오.

  • (t1. * f) (t2, ..., tN) f가 클래스 T의 멤버 함수에 대한 포인터이고 t1이 T 유형의 오브젝트이거나 T 유형의 오브젝트에 대한 참조이거나 T로부터 유도 된 유형의 객체;
  • f가 클래스 T의 멤버 함수에 대한 포인터이고 t1이 이전 항목에서 설명한 유형 중 하나가 아닌 경우 ((* t1). * f) (t2, ..., tN);
  • n == 1이고 f가 클래스 T의 멤버 데이터에 대한 포인터이고 t 1이 T 유형
    의 오브젝트 또는 T 유형의 오브젝트에 대한 참조 또는 다음
    에서 파생 된 유형 의 오브젝트에 대한 참조 인 경우 t1. * f 티;
  • (* t1). * f N == 1 일 때 f는 클래스 T의 멤버 데이터에 대한 포인터이고 t 1은 이전 항목에서 설명한 유형 중 하나가 아닙니다.
  • 다른 모든 경우에는 f (t1, t2, ..., tN).

내가 지적하고자하는 또 다른 일반적인 사실은 기본적으로 스레드 생성자가 전달 된 모든 인수를 복사한다는 것입니다. 그 이유는 인수가 호출 스레드보다 오래 지속되어야하기 때문에 인수를 복사하면 보장됩니다. 대신 실제로 참조를 전달하려면에서 std::reference_wrapper만든을 사용할 수 있습니다 std::ref.

std::thread (foo, std::ref(arg1));

이렇게하면 스레드가 작동 할 때 인수가 계속 존재하도록 보장 할 것입니다.


위에서 언급 한 모든 내용은 std::async및에 적용 할 수 있습니다 std::bind.


1
적어도 이런 식으로 컴파일됩니다. 왜 두 번째 인수로 인스턴스를 전달하는지 모르겠습니다.
abergmeier

15
@LCID : std::thread생성자 의 다중 인수 버전은 인수가에 전달 된 것처럼 작동합니다 std::bind. 멤버 함수를 호출하려면 첫 번째 인수 std::bind는 적절한 유형의 객체에 대한 포인터, 참조 또는 공유 포인터 여야합니다.
Dave S

생성자가 암시 적으로 작동한다는 점에서 어디서 가져 옵니까 bind? 어디서나 찾을 수 없습니다.
Kerrek SB

3
비교 @KerrekSB, [thread.thread.constr] P4와 [func.bind.bind] P3, 의미는 멤버 함수를 호출하는 방법을 정의하는 INVOKE의 의사의 관점에서 정의 된 매우 유사하다
조나단 Wakely

4
첫 번째 매개 변수로 정적 멤버 함수가 아닌 것은 클래스의 인스턴스를 가져옵니다 (프로그래머에게는 보이지 않음).이 함수를 원시 함수로 전달하면 컴파일 및 선언 불일치 중에 항상 문제가 발생합니다.
zoska

100

C ++ 11을 사용하고 있기 때문에 lambda-expression은 훌륭하고 깨끗한 솔루션입니다.

class blub {
    void test() {}
  public:
    std::thread spawn() {
      return std::thread( [this] { this->test(); } );
    }
};

때문에 this->생략 할 수 있습니다, 그것은하는 단축 될 수있다 :

std::thread( [this] { test(); } )

아니면 그냥

std::thread( [=] { test(); } )

8
일반적으로 std::move지역 변수를 값으로 반환 할 때는 사용하지 않아야 합니다. 이것은 실제로 RVO를 금지합니다. 값없이 (이동없이) 반환하면 컴파일러는 RVO를 사용할 수 있으며 표준이 아닌 경우 이동 의미론을 호출해야한다고 말합니다.
zmb

@zmb, VC10에서 코드를 컴파일하려는 경우를 제외하고 리턴 유형이 CopyConstructable이 아닌 경우 이동해야합니다.
abergmeier

6
RVO는 여전히 이동 의미론보다 더 나은 코드를 생성하며 사라지지 않습니다.
Jonathan Wakely

2
주의하십시오 [=]. 그것으로 당신은 실수로 큰 객체를 복사 할 수 있습니다. 일반적으로 또는 사용 하는 코드 냄새 입니다. [&][=]
rustyx

3
@Everyone 여기가 스레드라는 것을 잊지 마십시오. 이는 람다 함수가 컨텍스트 범위보다 오래 지속될 수 있음을 의미합니다. 따라서 참조 별 캡처 ( [&])를 사용하면 매달린 참조와 같은 버그가 발생할 수 있습니다. (예를 들어, std::thread spawn() { int i = 10; return std::thread( [&] { std::cout<<i<<"\n"; } ); })
RnMss

29

여기 완전한 예가 있습니다

#include <thread>
#include <iostream>

class Wrapper {
   public:
      void member1() {
          std::cout << "i am member1" << std::endl;
      }
      void member2(const char *arg1, unsigned arg2) {
          std::cout << "i am member2 and my first arg is (" << arg1 << ") and second arg is (" << arg2 << ")" << std::endl;
      }
      std::thread member1Thread() {
          return std::thread([=] { member1(); });
      }
      std::thread member2Thread(const char *arg1, unsigned arg2) {
          return std::thread([=] { member2(arg1, arg2); });
      }
};
int main(int argc, char **argv) {
   Wrapper *w = new Wrapper();
   std::thread tw1 = w->member1Thread();
   std::thread tw2 = w->member2Thread("hello", 100);
   tw1.join();
   tw2.join();
   return 0;
}

g ++로 컴파일하면 다음과 같은 결과가 나타납니다.

g++ -Wall -std=c++11 hello.cc -o hello -pthread

i am member1
i am member2 and my first arg is (hello) and second arg is (100)

10
OP 질문과 실제로 관련이 없지만 왜 랩퍼를 힙에 할당하고 할당을 해제하지 않습니까? java / c # 배경이 있습니까?
Alessandro Teruzzi

delete힙에서 메모리를 잊지 마십시오 :)
Slack Bot

19

@ hop5와 @RnMss는 C ++ 11 람다 사용을 제안했지만 포인터를 다루는 경우 직접 사용할 수 있습니다.

#include <thread>
#include <iostream>

class CFoo {
  public:
    int m_i = 0;
    void bar() {
      ++m_i;
    }
};

int main() {
  CFoo foo;
  std::thread t1(&CFoo::bar, &foo);
  t1.join();
  std::thread t2(&CFoo::bar, &foo);
  t2.join();
  std::cout << foo.m_i << std::endl;
  return 0;
}

출력

2

이 답변 에서 다시 작성된 샘플은 다음과 같습니다.

#include <thread>
#include <iostream>

class Wrapper {
  public:
      void member1() {
          std::cout << "i am member1" << std::endl;
      }
      void member2(const char *arg1, unsigned arg2) {
          std::cout << "i am member2 and my first arg is (" << arg1 << ") and second arg is (" << arg2 << ")" << std::endl;
      }
      std::thread member1Thread() {
          return std::thread(&Wrapper::member1, this);
      }
      std::thread member2Thread(const char *arg1, unsigned arg2) {
          return std::thread(&Wrapper::member2, this, arg1, arg2);
      }
};

int main() {
  Wrapper *w = new Wrapper();
  std::thread tw1 = w->member1Thread();
  tw1.join();
  std::thread tw2 = w->member2Thread("hello", 100);
  tw2.join();
  return 0;
}

0

일부 사용자는 이미 답변을했으며 매우 잘 설명했습니다.

스레드와 관련된 몇 가지 사항을 추가하고 싶습니다.

  1. functor 및 thread 작업 방법 아래 예를 참조하십시오.

  2. 스레드는 객체를 전달하는 동안 객체의 자체 사본을 만듭니다.

    #include<thread>
    #include<Windows.h>
    #include<iostream>
    
    using namespace std;
    
    class CB
    {
    
    public:
        CB()
        {
            cout << "this=" << this << endl;
        }
        void operator()();
    };
    
    void CB::operator()()
    {
        cout << "this=" << this << endl;
        for (int i = 0; i < 5; i++)
        {
            cout << "CB()=" << i << endl;
            Sleep(1000);
        }
    }
    
    void main()
    {
        CB obj;     // please note the address of obj.
    
        thread t(obj); // here obj will be passed by value 
                       //i.e. thread will make it own local copy of it.
                        // we can confirm it by matching the address of
                        //object printed in the constructor
                        // and address of the obj printed in the function
    
        t.join();
    }

동일한 것을 달성하는 다른 방법은 다음과 같습니다.

void main()
{
    thread t((CB()));

    t.join();
}

그러나 객체를 참조로 전달하려면 아래 구문을 사용하십시오.

void main()
{
    CB obj;
    //thread t(obj);
    thread t(std::ref(obj));
    t.join();
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.