Re: MT Design Question
"Scott Meyers" <NeverRead@aristeia.com>
No, the poll you suggested is a different kind of animal. What said here
is internal in the promise and is done before proceeding with useful
fork -- to provide early exit. IOW that is the cooperative way of thread
canceling.
I'm afraid I don't follow you here. Can you elaborate?
From past experience I recall only 3 methods for thread cancelation:
1. you have an API thak kills a thread. Dox warns you that you shall not use
it. using it removes the thread execution from the scheduler, while it can
be in any state, so you have a good chance to leak resources, and
considerable chance to be in a critical session or something, leaving locked
mutexes, etc, making the program unstable. I ever used it only in
program shutdown code if other ways failed (the thread go stuck due to some
design error...)
2. thread cancel triggering exception. some systems (maybe java?) define
points where the runtime checks the request on the thread and emits a
specific exception. So the programmer have some chance to juggle with
finally{} and recover -- certainly say goodbye to NOTHROW concept...
3. the cooperative way I was talking about: the thread itself watches a
variable or condition that is set outside, and interprets it as a request to
abandon whatever is done and exit ASAP.
The ponts where it looks at the variable is left to the thread, and the
method of access varies. One common way is to have a pthread mutex/cond
pair or on W32 an Event (optionally with a mutex) and the usual
producer/consumer queue. Having the cancel req either in-band as a message
in the queue, or a separate variable that is checked along. The mess
begins when the processing involves other kind of blocking too, like waiting
socket, file IO, etc. A common way to fight complexity is to give in, and
use some timeout in blocks, then check, say every 1 sec.
The situation you described is way more friendly: you have a process without
external blocking, and a natural loop with reasonably short processing
steps, so here the best way is just read a variable. cond/event is not
needed. For correctness you still need a mutex or at least a membar for the
regular case, but atomic<> stuff have those semantics in the pack
implicitly. So you can read it before visiting every next node. (if the
visit is too short AND atomics are hosed on the particular system, there may
be a counter for every Nth, I doubt it would pay off).
atomic<bool> is good to send in a request to stop. You can use an
atomic<int> as a kinda semaphore or completion count to report status too.
Say, start with 0, and every thread completed does an increment. For your
situation as soon as it is at 3 work is done. certainly that leaves you with
your original problem in the main thread what to do until all bumps
happened.
Doing a poll from this, really IDLE thread is really not good. You already
fiddled with cond+fake mutex, that is a working way. I could suggest an
alternative: use just a mutex.
1. main thread sets variable atomic<int> to N that is the number of threads
(N=3 in your example.
2. create and lock a mutex
3. launch N threads. on exit each decrements the variable, if the result is
zero unlocks the mutex. (They use the cancel signal to each other too
certainly.)
4. main thread waits on the mutex.
Unless I mislooked something you proceed on the main thread exactly when
appropriate. As I imagine the setup there may be other problems: if the
objects for the threads are local in the function frame, you may have them
auto-destroyed while the thread is not completely finished. I generally
prefer joins to avoid that situation easiest.