Threads and copy constructors...

Discussion in 'C++' started by oneflewover, Feb 18, 2012.

  1. oneflewover

    oneflewover Guest

    In the code snippet below, I have one functor that I pass to one
    thread. As expected, the constructor is called once, but the copy
    constructor is called four times! As a result, the destructor is
    called five times.

    Can someone please explain to me why the copy constructor is called
    four times? Thanks.

    ===================================================

    class c1
    {
    public:

    c1() { }

    c1(const c1& c) { }

    void operator()() { }

    ~c1() { }
    };

    int main()
    {
    c1 c; // constructor called once here

    //
    // copy constructor called four times here; why?
    //
    boost::thread t1( c1 );

    //
    // destructor called four times here
    //
    t1.join();

    //
    // destructor called once here
    //
    }
     
    oneflewover, Feb 18, 2012
    #1
    1. Advertising

  2. oneflewover

    Marc Guest

    oneflewover wrote:

    > In the code snippet below, I have one functor that I pass to one
    > thread. As expected, the constructor is called once, but the copy
    > constructor is called four times! As a result, the destructor is
    > called five times.
    >
    > Can someone please explain to me why the copy constructor is called
    > four times? Thanks.
    >
    > ===================================================
    >
    > class c1
    > {
    > public:
    >
    > c1() { }
    >
    > c1(const c1& c) { }
    >
    > void operator()() { }
    >
    > ~c1() { }
    > };
    >
    > int main()
    > {
    > c1 c; // constructor called once here
    >
    > //
    > // copy constructor called four times here; why?
    > //
    > boost::thread t1( c1 );
    >
    > //
    > // destructor called four times here
    > //
    > t1.join();
    >
    > //
    > // destructor called once here
    > //
    > }


    I think you want to complain on the boost mailing-list or on their bug
    tracker. In C++11, they forward this argument between internal
    functions by rvalue-reference, so you probably only get one copy and
    one move. In C++03, they pass it by value.
     
    Marc, Feb 18, 2012
    #2
    1. Advertising

  3. Am 18.02.2012 01:21, schrieb oneflewover:
    > In the code snippet below, I have one functor that I pass to one
    > thread. As expected, the constructor is called once, but the copy
    > constructor is called four times! As a result, the destructor is
    > called five times.
    >
    > Can someone please explain to me why the copy constructor is called
    > four times? Thanks.
    >
    > ===================================================
    >
    > class c1
    > {
    > public:
    >
    > c1() { }
    >
    > c1(const c1& c) { }
    >
    > void operator()() { }
    >
    > ~c1() { }
    > };
    >
    > int main()
    > {
    > c1 c; // constructor called once here
    >
    > //
    > // copy constructor called four times here; why?
    > //
    > boost::thread t1( c1 );


    boost::thread t1( boost::cref(c1) );

    >
    > //
    > // destructor called four times here
    > //
    > t1.join();
    >
    > //
    > // destructor called once here
    > //
    > }
    >



    Peter
     
    Peter Remmers, Feb 20, 2012
    #3
  4. Am 20.02.2012 20:33, schrieb Peter Remmers:
    > Am 18.02.2012 01:21, schrieb oneflewover:
    >> In the code snippet below, I have one functor that I pass to one
    >> thread. As expected, the constructor is called once, but the copy
    >> constructor is called four times! As a result, the destructor is
    >> called five times.
    >>
    >> Can someone please explain to me why the copy constructor is called
    >> four times? Thanks.
    >>
    >> ===================================================
    >>
    >> class c1
    >> {
    >> public:
    >>
    >> c1() { }
    >>
    >> c1(const c1& c) { }
    >>
    >> void operator()() { }
    >>
    >> ~c1() { }
    >> };
    >>
    >> int main()
    >> {
    >> c1 c; // constructor called once here
    >>
    >> //
    >> // copy constructor called four times here; why?
    >> //
    >> boost::thread t1( c1 );

    >
    > boost::thread t1( boost::cref(c1) );


    I was too quick. After a second reading, I noticed that you use c1 as a
    thread functor. boost::cref is good for passing const references as
    arguments to the thread function, but I don't think this is a good idea
    for the thread functor itself.

    I would write a member function that is the thread function:

    class c1
    {
    [...]
    void run() {}
    [...]
    };

    and then start it by:

    boost::thread(&c1::run, &c);

    A possibility is to start the thread in c1::c1() and join it in
    c1::~c1(). You would have to come up with a way to tell the run function
    to exit if it is supposed to run as long as the c1 instance lives. If
    run() is just meant to do one lengthy operation with a finite duration,
    it may be ok to just join the thread.


    I think what I initially wrote would actually work if operator()() were
    const. Else using boost::ref(c1) to pass a non-const reference may also
    work.

    But if you're going to use a functor, then I'd prefer

    boost::thread t1( c1() );

    and live with it being copied a couple of times.
    Otherwise you would have to make sure to join the thread before c goes
    out of scope;


    Peter
     
    Peter Remmers, Feb 20, 2012
    #4
  5. oneflewover

    oneflewover Guest

    On 2012-02-20 19:53:27 +0000, Peter Remmers said:

    > I was too quick. After a second reading, I noticed that you use c1 as a
    > thread functor. boost::cref is good for passing const references as
    > arguments to the thread function, but I don't think this is a good idea
    > for the thread functor itself.
    >
    > I would write a member function that is the thread function:
    >
    > class c1
    > {
    > [...]
    > void run() {}
    > [...]
    > };
    >
    > and then start it by:
    >
    > boost::thread(&c1::run, &c);
    >
    > A possibility is to start the thread in c1::c1() and join it in
    > c1::~c1(). You would have to come up with a way to tell the run function
    > to exit if it is supposed to run as long as the c1 instance lives. If
    > run() is just meant to do one lengthy operation with a finite duration,
    > it may be ok to just join the thread.
    >
    >
    > I think what I initially wrote would actually work if operator()() were
    > const. Else using boost::ref(c1) to pass a non-const reference may also
    > work.
    >
    > But if you're going to use a functor, then I'd prefer
    >
    > boost::thread t1( c1() );
    >
    > and live with it being copied a couple of times.
    > Otherwise you would have to make sure to join the thread before c goes
    > out of scope;



    Thanks, Peter, for the detailed info. I had actually made a mistake
    when I pasted the code snippet. It should have read:

    boost::thread t1( c );


    In other words, I'm passing the object (by value), after constructing
    it. Sorry about that.

    I understand the benefits of using boost::ref(). I also understand the
    benefits of starting the thread in other member functions. However, I
    am still trying to understand why the "copy constructor" is called four
    times in my example. With boost::ref(), the copy constructor is called
    zero times (as expected). I just tried that. Without boost::ref(), I
    would expect the copy constructor to be called exactly once (when the
    thread object copies my object by value).

    Do you know why the copy constructor is called four times? Thanks much.
     
    oneflewover, Feb 20, 2012
    #5
  6. Am 20.02.2012 23:28, schrieb oneflewover:
    > On 2012-02-20 19:53:27 +0000, Peter Remmers said:
    >
    >> I was too quick. After a second reading, I noticed that you use c1 as a
    >> thread functor. boost::cref is good for passing const references as
    >> arguments to the thread function, but I don't think this is a good idea
    >> for the thread functor itself.
    >>
    >> I would write a member function that is the thread function:
    >>
    >> class c1
    >> {
    >> [...]
    >> void run() {}
    >> [...]
    >> };
    >>
    >> and then start it by:
    >>
    >> boost::thread(&c1::run, &c);
    >>
    >> A possibility is to start the thread in c1::c1() and join it in
    >> c1::~c1(). You would have to come up with a way to tell the run function
    >> to exit if it is supposed to run as long as the c1 instance lives. If
    >> run() is just meant to do one lengthy operation with a finite duration,
    >> it may be ok to just join the thread.
    >>
    >>
    >> I think what I initially wrote would actually work if operator()() were
    >> const. Else using boost::ref(c1) to pass a non-const reference may also
    >> work.
    >>
    >> But if you're going to use a functor, then I'd prefer
    >>
    >> boost::thread t1( c1() );
    >>
    >> and live with it being copied a couple of times.
    >> Otherwise you would have to make sure to join the thread before c goes
    >> out of scope;

    >
    >
    > Thanks, Peter, for the detailed info. I had actually made a mistake
    > when I pasted the code snippet. It should have read:
    >
    > boost::thread t1( c );
    >
    >
    > In other words, I'm passing the object (by value), after constructing
    > it. Sorry about that.


    Just don't forget that if you do that, the thread will not access c's
    members but those of a copy, so there is no use in defining c outside
    the thread construction...

    >
    > I understand the benefits of using boost::ref(). I also understand the
    > benefits of starting the thread in other member functions. However, I
    > am still trying to understand why the "copy constructor" is called four
    > times in my example. With boost::ref(), the copy constructor is called
    > zero times (as expected). I just tried that. Without boost::ref(), I
    > would expect the copy constructor to be called exactly once (when the
    > thread object copies my object by value).
    >
    > Do you know why the copy constructor is called four times? Thanks much.


    Use a debugger and step through the code. You'll see :)
    The boost::thread takes the functor by value, so there's the first copy,
    just for the call. It then stores it in some thread detail info class,
    also by value. This is done via some layers of wrappers and helper
    functions (make_thread_info()), each of them take it by value. So
    there's several copies.

    This is because with C++98 there's no perfect forwarding. With C++11,
    boost::thread can use rvalue references, so the functor would be moved
    instead of copied.


    Peter
     
    Peter Remmers, Feb 21, 2012
    #6
  7. oneflewover

    oneflewover Guest

    On 2012-02-21 08:50:21 +0000, Peter Remmers said:

    > Just don't forget that if you do that, the thread will not access c's
    > members but those of a copy, so there is no use in defining c outside
    > the thread construction...


    Good point.


    > Use a debugger and step through the code. You'll see :)
    > The boost::thread takes the functor by value, so there's the first copy,
    > just for the call. It then stores it in some thread detail info class,
    > also by value. This is done via some layers of wrappers and helper
    > functions (make_thread_info()), each of them take it by value. So
    > there's several copies.
    >
    > This is because with C++98 there's no perfect forwarding. With C++11,
    > boost::thread can use rvalue references, so the functor would be moved
    > instead of copied.


    Great; thanks! That was the answer I was looking for. It's also a
    convincing argument for using boost::ref().
     
    oneflewover, Feb 21, 2012
    #7
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Dave Rudolf
    Replies:
    12
    Views:
    8,373
    Martijn Lievaart
    Feb 6, 2004
  2. Jeremy Smith
    Replies:
    2
    Views:
    611
    Jeremy Smith
    Aug 3, 2006
  3. Jess
    Replies:
    5
    Views:
    626
    Ron Natalie
    Jun 7, 2007
  4. Peng Yu
    Replies:
    5
    Views:
    407
    Juha Nieminen
    Sep 19, 2008
  5. srp113
    Replies:
    3
    Views:
    489
Loading...

Share This Page