
Le 13/10/12 14:40, Vicente J. Botet Escriba a écrit :
2012/10/13 Rob Stewart <robertstewart@comcast.net>
On Oct 12, 2012, at 4:24 PM, "Vicente J. Botet Escriba" < vicente.botet@wanadoo.fr> wrote:
2012/10/10 Vicente J. Botet Escriba <vicente.botet@wanadoo.fr>
Le 10/10/12 10:23, Andrzej Krzemienski a écrit :
Hi, > I can see in the release notes that in Boost 1.52 boost::thread's > destructor calls terminate if joinable, in order to conform to > C++11 > specification. I am not sure if this is the best course of action. > My understanding -- form the C++ Committee papers and informal > presentations -- is that the reason for introducing a 'terminating > destructor' was the lack of thread cancellation/interruption > functionality. > Thread interruption is supposed to be the preferred behavior for
Le 10/10/12 14:56, Andrzej Krzemienski a écrit : thread's
> destructor. std::thread does not support interruption (for some reasons), > but boost::thread does (this is already a departure from C++11), so > shouldn't the latter prefer to interrupt a joinable thread in the > destructor? > yes this is a possible alternative to the standard behavior. But what to do after interrupting, joining? What others think? Anthony?
My preference would be to join after the interruption. If I remember correctly, the argument against joining for std::thread is that there would be an unexpected hang upon reaching the end of the scope. That seems a reasonable argument.
The argument against detaching for std::thread is that the detached thread may be holding references to automatic variables defined in the scope of the forking thread that we are now exiting. That seems like too much nannyism. C++ already has many ways to do similar things, so why be so concerned with this one case?
So are you saying the thread should detach on scope exit rather than terminate?
joining is mitigated, while the argument against detaching still holds. I've though more about your suggestion, and it seems to me that it has
I believe that with thread interruption in place the argument against the same drawbacks as joining directly. The point is that interrupting a thread doesn't ensures that the thread will finish soon, as the thread could not call any interruption point, so that joining them later could take an undefined time.
I agree. Hanging like that during stack unwinding would be painful. At least with terminate, you'd realize the problem.
MT requires knowledge and skill. Too much handholding gets in the way and encourages naive users to get in over their head.
If someone wants a thread to interrupt and join on destruction, they need only put the thread object in another object and have that destructor interrupt and join.
The approach to Boost libraries' interface I encountered so far is that they provide the simple interface for the newbies (that does a bit of nanying) and a more complicate interface for the professionals. To me, the interruption in boost::thread is like a good default behavior for the beginners. The professionals would surely not use naked std::thread. I would propose just the opposite. If you are sure you want your thread handle to terminate the program in destructor, write your wrapper class yourself, and terminate there, and use the joining destructor for the default boost::thread interface (for beginners). I believe that for beginners, termination may not be of use to identify the source of the problem (I may be wrong though).
Yes, interrupting does not guarantee that you will not hang, however, it *mitigates* the hang argument. It puts different balance in the choice between joining and terminating. Perhaps the choice to terminate is still the best one.
My problem with terminating destructor is that there is no safe way to use it other than wrap it in another class that takes a different approach in the destructor. This makes std::thread (and now boost::thread) like a pair of operators new and delete, which require that you do not use them directly, but write some wrapper instead. Well, this might be an acceptable situation after all. I would really prefer if Boost.Thread behaves like std::threads as much as possible so that moving from one to the other don't introduce many surprises. I don't know yet if this is doable but I would like that the interruption mechanism is defined on top of the design of std::threads, that is non cancelable threads, so that the user don't
Le 13/10/12 13:59, Andrzej Krzemienski a écrit : pay for what he don't use. There is already a feature request to provide a condition variable that is as efficient as the user could have using the pthread interface.
What bothers me is that I have seen many introductions to using threads in C++ that give such example:
int work( Operation op, Input i1, Input i2 ) { int ans1 thread th{ [&]{ans1 = op(i1);} };
int ans2 = op(i2); return ans1 + ans2; }
It looks like this code makes sense. But it is wrong. You could argue that one should start teaching multi-threading with async(), but somehow anyone chooses to start with thread. Well, I guess the Boost.thread documentation could help on this sense, but I'm not a big writer. I will see however what I can do.
Interrupting and joining destructor would be good for novices. And it enables the scoped (RAII-like) reasoning about threads. I don't think this default behavior is the good one, even for beginners. Of course all this is quite subjective.
What do you think of adding a thread_guard class that could interrupt and join on the destructor? Example taken from "C++ Concurrency in Action" from A. Williams #include <thread> class thread_guard { std::thread& t; public: explicit thread_guard(std::thread& t_): t(t_) {} ~thread_guard() { if(t.joinable()) { t.join(); } } thread_guard(thread_guard const&)=delete; thread_guard& operator=(thread_guard const&)=delete; }; void do_something(int& i) { ++i; } struct func { int& i; func(int& i_):i(i_){} void operator()() { for(unsigned j=0;j<1000000;++j) { do_something(i); } } }; void do_something_in_current_thread() {} void f() { int some_local_state; func my_func(some_local_state); std::thread t(my_func); thread_guard g(t); do_something_in_current_thread(); } The book describes also a scoped_thread that do something similar #include <thread> #include <utility> class scoped_thread { std::thread t; public: explicit scoped_thread(std::thread t_): t(std::move(t_)) { if(!t.joinable()) throw std::logic_error("No thread"); } ~scoped_thread() { t.join(); } scoped_thread(scoped_thread const&)=delete; scoped_thread& operator=(scoped_thread const&)=delete; }; void f() { int some_local_state; scoped_thread t(std::thread(func(some_local_state))); do_something_in_current_thread(); } Would something like these simple classes respond to your needs? Best, Vicente