I am going through the book Concurrency in action for C++. There is an example there of a class called scoped_thread (pg 27), which ensures the RAII idiom and the thread has joined before its respective end of scope.
This current example does not allow it to be used with functions that best requires the move operator ,such as the emplace_back member function for vector (?). This way you can call the class constructor and possibly allow better optimization than push_back. As such, I wanted to add move constructor in class scoped_thread.
Before I present the material, the two questions are as follows
- What's the reason for the non-parallel constructor and move constructor calls? how to reduce them?
- Is my move constructor correct?
- I am now calling
joinable()two times. Originally it was in the constructor. But since I created the move constructor, I've had to check forjoinable()in the destructor as well. This may tie in with question 2 if my move constructor is not good
See EDIT at bottom of page also before continuing
Without further ado, here is my class code (link provided below for full code in online compiler)
CLASS SCOPED_THREAD
#define PRINT(X) std::cout << X << std::endl;
static unsigned dtor_count = 0;
static unsigned ctor_count = 0;
static unsigned mtor_count = 0;
class scoped_thread {
thread t;
public:
explicit scoped_thread(thread t_) :
t(std::move(t_))
{
if (!t.joinable())
throw std::logic_error("No thread!");
PRINT("Called scoped_thread CTor");
ctor_count++;
}
~scoped_thread()
{
if (t.joinable())
t.join();
PRINT("Called scope_thread DTor");
dtor_count++;
}
scoped_thread(const scoped_thread&) = delete; // copy CTor
scoped_thread& operator=(const scoped_thread&) = delete; // copy init
scoped_thread(scoped_thread&& s) {
t = std::move(s.t);
mtor_count++;
};
};
The static variables are used as a counter for the number of calls to the constructor, destructor and move constructor.
Here is an example use. (Note: the commented section is what could have been achieved without this class)
EXAMPLE USE
void do_work(int i) { PRINT("Made Thread : " << i); };
void thread_vector_example()
{
// normal version: have to call thread join
/*
std::vector<std::thread> threads;
for (unsigned i = 0; i < 10; ++i)
threads.push_back(std::thread(do_work, i));
std::for_each(begin(threads), end(threads),
std::mem_fn(&std::thread::join));
*/
std::vector<scoped_thread> sthreads;
for (unsigned i = 0; i < 10; ++i) {
sthreads.emplace_back(std::thread(do_work, i));
}
}
-
From the g++ compiler (link given below) with options
g++ -std=c++14 -O2 -Wall -pthreadthe results are as follows- constructors called: 10
- destructors called: 25
- move constructors called: 15
-
From the Visual Studio C++ compiler (standard options)
- constructors called: 10
- destructors called: 35
- move constructors called: 25
link to file--> http://ift.tt/1busNXK
EDIT
After calling the reserve function i.e. sthreads.reserve(10), I can have a more better version where it calls constructor and destructor 10 times only. However, there still is an issue when I remove the move constructor from the class, the code will not compile
Aucun commentaire:
Enregistrer un commentaire