I'm trying to pass multiple arguments, one of which is an ofstream
object, to multiple threads using the C++11
standard.
I want to pass an ofstream
object because I want every thread to write in a different output file.
I'm initializing the threads and output streams in this way:
std::thread execution_threads[NUMBER_OF_THREADS]; // creating multiple empty threads
std::ofstream output_files[NUMBER_OF_THREADS]; // Empty output streams
// Opening and initializing the output files
Each thread executes a function that takes two arguments:
void execution(int thread_id, std::ofstream& output_file)
So I've looked around and I've read that in C++11
when a function func
has multiple arguments a,b,c,d
there's no need to use a struct
and you can pass them just by writing std::thread t(func, a,b,c,d);
. So I wrote this loop in order to launch the threads:
for (int i = 0; i < utils::NUMBER_OF_THREADS; i++) {
execution_threads[i] = std::thread(execution, i, output_files[i]);
}
The thing is that this code doesn't compile with this error:
Call to implicitly-deleted copy constructor of
'typename decay<basic_ofstream<char, char_traits<char> > &>::type'
(aka 'std::__1::basic_ofstream<char, std::__1::char_traits<char> >')
While if I use a struct
as input in this way, everything works fine:
// Struct definition
struct thread_input {
int thread_id;
std::ofstream& output_file;
};
// This is the function that now has only one argument
void execution(struct thread_input input)
// And this is the loop that launches the threads
for (int i = 0; i < utils::NUMBER_OF_THREADS; i++) {
struct thread_input input = {i, output_files[i]};
execution_threads[i] = std::thread(execution, input);
}
// join() functions for each thread and end of the program
In this way everything works fine it compiles and it runs perfectly. But I really don't get why the compiler tells me that I'm trying to use the deleted copy-constructor if I use the other method.
Thank you for your help.
std::thread
stores a copy of its arguments. When you pass it a non-copyable object like a std::ofstream
it will complain.
You have two options:
1) Don't store an array of std::ofstream
objects; just let your threads store their own streams. In this case, there's no need to copy the stream (just a move, which is fine):
for (int i = 0; i < utils::NUMBER_OF_THREADS; i++) {
execution_threads[i] = std::thread(execution, i, std::ofstream{});
//^^^^^^^^^^^^^^^ anonymous temporary
}
Of course, in this case you could just have the thread construct its own stream (maybe just pass in the filename).
2) Pass a std::reference_wrapper<std::ofstream>
to your thread. std::reference_wrapper<T>
is an object that holds a reference to T
and has an implicit conversion to T&
, so you'll just end up copying the reference instead of the stream itself. You can use the std::ref
factory to deduce T
and reduce typing:
for (int i = 0; i < utils::NUMBER_OF_THREADS; i++) {
execution_threads[i] = std::thread(execution, i, std::ref(output_files[i]));
//^^^^^^^^ std::ref added
}
This leaves you with all of the same ownership and lifetime issues that passing a struct containing a std::ofstream&
would have (after all, that's all std::reference_wrapper
is). It's up to you to make sure your output_files
array survives until all of your threads have finished with it.