(C++)스레드에서 인스턴스의 멤버 변수를 이용하는 경우의 메모리 관리

tpe4의 이미지

안녕하세요.

요즘 스레드를 활용한 클래스를 하나 제작중인데, 궁금한 점이 있어 질문을 올립니다.

class A
{
 void Run(); // boost::thread를 이용해 자신의 RunFunc 멤버 함수를 하나의 스레드로 실행
 void RunFunc(); // 멤버 변수 object에 접근해서 이것저것 합니다.
 
 Object object;
}
 
void B
{
 A a;
 a.Run();
}

이런 식으로 활용을 할 경우 인스턴스 a와 object가 소멸되어도 RunFunc 함수의 스레드가 계속 a의 멤버 변수 object를 참조하기 때문에 문제가 발생하게 됩니다. 스레드를 사용하는 기본적인 패턴 같은데, 실제로는 어떻게 처리를 하는게 가장 좋을지 마땅한 방법을 모르겠네요.

A의 소멸자에서 스레드를 강제로 종료시키는게 가장 나을 것 같지만 스레드를 외부에서 kill하는 것은 별로 좋지 않다는 이야기도 있고, 그렇다고 해서 object를 힙 영역에 생성시키는 것도 별로 좋은 방법 같지가 않네요. 최적의 방법은 무엇이 있을까요?

kukyakya의 이미지

boost::thread 객체를 멤버변수로 들고 계시다가 A의 소멸자에서 join 시켜주시면 쓰레드가 돌아가고 있는 동안은 객체가 유지될텐데 A의 소멸자가 블럭되면 안되는 상황인가요?

tpe4의 이미지

답변 감사합니다. 왠지 소멸자에서 블럭킹을 한다는 게 좀 찝찝하더라구요. 멤버 변수로 스레드 종료 플래그를 만들고 소멸자에서 플래그를 셋팅하고 join하는 방법도 있겠네요. 강제로 kill 하는 것보다는 나을지..

oosap의 이미지

어딘가 오해가 있지 않나요?
객체의 소멸이 되려면 멤버함수중 수행중인 함수가 없을 때라야 가능할 텐데요... 제가 오해하는건가요?

http://yesarang.tistory.com/70

이것은 자바의 쓰레드를 모방한 C++ 쓰레드 클래스 입니다. 저는 가끔 실무에서도 사용합니다.
제가 부스트를 아직 몰라서.... 그러데 쓰레드의 구현을 어떻게 하고 어떻게 쓸지를 자세히 논하고 제시하고 있습니다.
쓰레드이기만 하면 된다라면 그냥 가져다 써도 될 정도예요..

Thanks for being one of those who care for people and mankind.
I'd like to be one of those as well.

oosap의 이미지

부스트를 쓰신다고 하셨는데 그렇다면 join 기능은 쓰레드 클래스에 이미 있을 것 같습니다. 소멸자에서 join을 하는 경우는 흔치 않은 것 같구요, 대신 쓰레드 객체를 생성, 런, 조인(wait) 의 순서로 사용합니다. 제가 위에서 소개드린 코드가 그렇구요, 부스트도 아마 그런 멤버 함수가 있을 테니 그냥 잘 쓰면 될 것 같아요... 제가 부스트를 본 적은 없지만 말입니다....

Thanks for being one of those who care for people and mankind.
I'd like to be one of those as well.

tpe4의 이미지

우선 수행중인 함수가 있더라도 객체는 소멸이 됩니다. 저도 깊게 아는 것이 아니라 확신은 못 하지만
어차피 스레드에서 실행을 시키는 것은 실제 존재하는 인스턴스 안에 있는 것이 아닌 전역으로 존재하는 클래스 멤버 함수에 대한 포인터일 뿐이므로... 하나의 인스턴스가 소멸되는 것과는 관계가 없을 듯 합니다.

boost::thread의 활용은 링크해주신 클래스와는 좀 차이가 있습니다. 내부 원리는 크게 차이가 안 날것 같지만... 또한 boost에도 join 함수가 있지만 애초에 A 클래스에서 RunFunc를 스레드로 실행시키는 이유가 비동기 실행을 구현하기 위해서라 join을 할 수는 없는 상황입니다.

oosap의 이미지

부스트를 본 적 없다는 걸 먼저 말씀드리면서,
pthread 에서는 쓰레드를 생성하고나서 곧바로 해야할 일이 있습니다. 조인하거나 디테치 하는 것입니다. 조인을 하면 말씀하신 것 처럼 병행성을 얻기 위해서는 또 다른 쓰레드가 필요해지지요. 디테치를 하면 더 이상 쓰레드의 종료를 기다릴 필요가 없어집니다. 쓰레드를 생성할 때 디태치 된 상태로 생성할 수 있는데 이런 경우 쓰레드의 생성 후에 별도로 디테치를 할 필요는 없습니다.
일단 공식처럼 알고 있어야 하는 것이 이것입니다.

그 다음 본문의 코드에서 문제점이 보이는군요. 쓰레드를 리소스라고 생각했을 때 코드상에서 쓰레드 클래스를 스택 객체로 만들었습니다. 더군다나 그 스택은 main 의 스택도 아닌 짧은 순간 명멸하는 함수의 스택인 것 같습니다. 바로 이 부분이 문제인 것 같습니다. 스택 객체로 하시려면 그 스택이 쓰레드 함수의 발생부터 소멸시 까지 유지되도록 하면 될 것 같습니다. 쓰레드의 수행이 종료될 때가지 스택이 유지될테니까요. 그렇게 하지 못한다면 std::auto_ptr 같은 소유권 전달이 가능한 포인터를 사용해서 생성한 쓰레드 객체의 소유권을 리턴해주어서 적절한 스택에서 그 스레드 객체가 존재하도록 하면 될 것 같습니다. 그것도 안된다면 객체를 스택이 아닌 힙에 두면 되죠. 하지만 이 경우 객체의 유출이나 이중 해제 등의 버그가 발생하지 않도록 꼼꼼히 코드를 작성하면 되죠.

그리고 제가 오해했던 부분이 맞는 것 같습니다.
님의 질문은 제가 최근에 공부하는 중이던 C++ 리소스 매니지먼트와 관련된 내용이라 관심이 가는 군요...

http://kldp.org/node/130327

Thanks for being one of those who care for people and mankind.
I'd like to be one of those as well.

oosap의 이미지

제가 오해했고 님 의견이 맞는 것 같다는 뜻입니다.

Thanks for being one of those who care for people and mankind.
I'd like to be one of those as well.

tpe4의 이미지

그저 객체가 소멸될 때 스레드가 종료되는 게 맞다고 생각했는데, 사실상 스레드에서 감지할 방법이 없기 때문에 객체가 메모리에 존재하는 시기를 컨트롤하는게 맞는 듯 합니다. 스마트 포인터를 활용하면 쉽게 처리가 될 것 같네요. 뭔가 더 단순한 방법이 없나 싶기는 하네요.

kukyakya의 이미지

shared_ptr을 활용해보시는건 어떠신가요?

#include <iostream>
#include <memory>
#include <thread>
 
volatile int count;
 
struct object {
  explicit object(int __v) : v(__v) { std::cout << __PRETTY_FUNCTION__ << std::endl; }
  ~object() { std::cout << __PRETTY_FUNCTION__ << std::endl; }
 
  int v;
};
 
class runner : public std::enable_shared_from_this<runner>
{
  public:
    typedef std::shared_ptr<runner> pointer;
 
  public:
    static pointer create(int __v) { return pointer(new runner(__v)); }
    ~runner() { std::cout << __PRETTY_FUNCTION__ << std::endl; }
 
    void run() {
      std::thread th(&runner::work, shared_from_this());
      th.detach();
    }
 
    void work() {
      std::cout << __PRETTY_FUNCTION__ << " has been started" << std::endl;
      for(count=0; count<100000000; ++count); // no std::this_thread::sleep* in GCC 4.6.2
      std::cout << __PRETTY_FUNCTION__ << " : obj.v = " << m_obj.v << std::endl;
      std::cout << __PRETTY_FUNCTION__ << " is quitting" << std::endl;
    }
 
  private:
    runner(int __v) : m_obj(__v) { std::cout << __PRETTY_FUNCTION__ << std::endl; }
 
  private:
    object m_obj;
};
 
int main() {
  {
    auto ptr = runner::create(5);
    ptr->run();
  }
  std::cout << __PRETTY_FUNCTION__ << " : end of the block" << std::endl;
  for(count=0; count<200000000; ++count); // no std::this_thread::sleep* in GCC 4.6.2
  std::cout << __PRETTY_FUNCTION__ << " : quitting" << std::endl;
}
tpe4의 이미지

객체의 소유권을 스레드에서 가질 수가 있군요. 스마트 포인터를 활용하는 상황에서는 굉장히 깔끔하게 처리할 수 있네요. 답변 감사드립니다.

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.