Threads and copy constructors...

O

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 );

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

//
// destructor called once here
//
}
 
M

Marc

oneflewover said:
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.
 
P

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) );
//
// destructor called four times here
//
t1.join();

//
// destructor called once here
//
}


Peter
 
P

Peter Remmers

Am 20.02.2012 20:33, schrieb Peter Remmers:
Am 18.02.2012 01:21, schrieb oneflewover:

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
 
O

oneflewover

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.
 
P

Peter Remmers

Am 20.02.2012 23:28, schrieb oneflewover:
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
 
O

oneflewover

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().
 

Ask a Question

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

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,989
Messages
2,570,207
Members
46,783
Latest member
RickeyDort

Latest Threads

Top