multithreading - C++ class with thread - destructor, constructor, move constructor? -
i going through book concurrency in action c++. there example there of class called scoped_thread
(pg 27), ensures raii idiom , thread has joined before respective end of scope.
this current example not allow used functions best requires move operator ,such emplace_back
member function vector
(?). way can call class constructor , possibly allow better optimization push_back
. such, wanted add move constructor in class scoped_thread
.
before present material, 2 questions follows
- what's reason non-parallel constructor , move constructor calls? how reduce them?
- is move constructor correct?
- i calling
joinable()
2 times. in constructor. since created move constructor, i've had checkjoinable()
in destructor well. may tie in question 2 if move constructor not good
see edit @ bottom of page before continuing
without further ado, here class code (link provided below 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 used counter number of calls constructor, destructor , move constructor.
here example use. (note: commented section have been achieved without class)
example use
void do_work(int i) { print("made thread : " << i); }; void thread_vector_example() { // normal version: have call thread join /* std::vector<std::thread> threads; (unsigned = 0; < 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; (unsigned = 0; < 10; ++i) { sthreads.emplace_back(std::thread(do_work, i)); } }
from g++ compiler (link given below) options
g++ -std=c++14 -o2 -wall -pthread
results follows- constructors called: 10
- destructors called: 25
- move constructors called: 15
from visual studio c++ compiler (standard options)
- constructors called: 10
- destructors called: 35
- move constructors called: 25
link file--> http://coliru.stacked-crooked.com/a/33ce9f3daab4dfda
edit
after calling reserve function i.e. sthreads.reserve(10)
, can have more better version calls constructor , destructor 10 times only. however, there still issue when remove move constructor class, code not compile
- what's reason non-parallel constructor , move constructor calls? how reduce them?
the reason 10 constructor calls construct 10 objects...
as you've figured out, reason move constructor calls reallocation of vector's internal storage due exceeding capacity , can rid of them reserving enough capacity before hand.
- is move constructor correct?
yes. once stop debugging constructor counts, scoped_thread(scoped_thread&& s) = default;
should suffice.
i calling joinable() 2 times. in constructor. since created move constructor, i've had check joinable() in destructor well. may tie in question 2 if move constructor not good
the possibility of being moved require test in destructor. since it's tested in destructor, there isn't point in testing in constructor in opinion.
however, there still issue when remove move constructor class, code not compile
even though use vector in way causes no moves (reserve space , emplace new objects), objects must nevertheless movable. compiler cannot check never exceed capacity during run time , accordingly not generate code reallocation depends on type being movable or copyable.
here's bonus idea since want avoid moves. don't move thread constructor construct in-place instead (you may still want provide construct moves thread):
template<class fun, class... args> explicit scoped_thread(fun&& f, args&&... args): t(std::forward<fun>(f), std::forward<args>(args)...) {} //... sthreads.emplace_back(do_work, i);
Comments
Post a Comment