Re: MT Design Question

Paavo Helde wrote:
> But there is a shared state, namely the results. When the worker thread
> has found some result, it has to write it somewhere where the main thread
> can find it.

This is what a future is for. As I noted, my plan is for each search function
to have its own future. In C++0x, callees write their result to a promise, and
callers retrieve the result through a future. The state shared by a promise and
future is internally synchronized, so there is no need for clients to use a
mutex with it.
> This location is shared and needs some MT protection, e.g. a
> mutex. You might also want to maintain a counter of finished worker
> threads to decide when they have all failed, this counter would be shared
> as well.

When all workers have finished, all their futures will be ready, so there is no
need for a counter to determine that (although it could be convenient).

Advertisements

On Aug 25, 12:24 am, Paavo Helde <> wrote:
> Scott Meyers <> wrote
> innews:i51fus$hft$:
> > Paavo Helde wrote:
> >> But there is a shared state, namely the results. When the
> >> worker thread has found some result, it has to write it
> >> somewhere where the main thread can find it.
> > This is what a future is for. As I noted, my plan is for
> > each search function to have its own future. In C++0x,
> > callees write their result to a promise, and callers
> > retrieve the result through a future. The state shared by
> > a promise and future is internally synchronized, so there is
> > no need for clients to use a mutex with it.
> I see. I overlooked the usage of C++0x future<> in the OP.
> I am not familiar with this feature, but it appears to be
> a higher level construct having internal synchronisation.

Yes. The intent, if I understand it correctly, is to
encapsulate the creation of the thread and its join. The
results data are set in the thread, and only read after the join
in the creating thread.

And again, IIUC, Scott's problem can be summarized as "you can
only wait for one future at a time". You can poll them, in
a loop, but Scott, very understandably, doesn't want to do that.

I'm not as familiar as I'd like to be with the proposed
threading in the standard, but I have handled a similar problem
with Posix threads: I made the threads detached (so no join was
available), and used a (single) condition to communicate, with
a table of results, one per thread.

James Kanze wrote:
> Yes. The intent, if I understand it correctly, is to
> encapsulate the creation of the thread and its join. The
> results data are set in the thread, and only read after the join
> in the creating thread.

This isn't quite true. The calling thread can read the future as soon as it's
set by the callee (via the corresponding promise). The callee thread may then
run for a while longer, e.g., to invoke destructors of local objects and
thread-local objects. If the thread runs detached, there may never be a join,
although that complicates matters at program shutdown.
> I'm not as familiar as I'd like to be with the proposed
> threading in the standard, but I have handled a similar problem
> with Posix threads: I made the threads detached (so no join was
> available), and used a (single) condition to communicate, with
> a table of results, one per thread.

This sounds like your design had the master thread wait until all worker threads
were done, i.e., until the table was completely filled in. Is that correct? My
problem is more along the lines of wanting to wake the master when any entry in
the table is filled in with a non-exception result. But that just pushes the
notification problem into the table object: how is it to know when a thread has
posted a non-exception result if it doesn't use polling?

Paavo Helde wrote:
> Which makes it more high-level in some sense. And this exception throwing
> is not useful anyway in this case as far as I can see. One does not want
> to trigger an exception if another thread can still produce a result

In the scenario I'm discussing, if one thread throws, it has no effect on other
threads. The exception for that thread will just be stored in the thread's
future. The other threads will continue to run.
> if all fail, one wants to throw a new exception instead (possibly
> combining information from all of the worker thread exceptions).

And the caller can do that if it wants to. It can just do a get() on each
future, catch any exception that may be present, then use whatever logic it
wants to to decide what, if anything, should be thrown from there.
> In other words, in my experience an ordinary mutex and condition would
> work nicely here, with a protected result location an and a thread
> counter.

So you'd run all searches concurrently, passing them all a common result
location to write to. What would you do if all ended by throwing an exception?
How would you distinguish "none of the searches found anything" from "all the
searches ended by throwing an exception"?

>> In other words, in my experience an ordinary mutex and condition would
>> work nicely here, with a protected result location an and a thread
>> counter.
>
> So you'd run all searches concurrently, passing them all a common result
> location to write to.

MT design is mostly about avoiding sharing as much as possible. It will
involve extra sync primitives, extra lock operations, task switches.
> What would you do if all ended by throwing an exception?

And also extra complexity in cases like that. Race for time and space. With
extra chances to overlook some interaction.

I would not start considering it before having multiple (per-thread)
locations is proved as an issue. What is hard to imagine even wth a system
without dynamic allocation.

"Paavo Helde" <> wrote in message
news:Xns9DDFEF607DD9Cmyfirstnameosapriee@216.196.109.131...
[...]
>
> If there is a way to wait for multiple future<> results, all this could
> be done by the master thread instead, which can then sort out all this
> ternary logic by itself. This might be a bit cleaner, especially in
> regard of dealing with orphaned threads (those not yet finished when the
> master thread returns the found result).

This pseudo-code will only work when a future is obtained from a promise:
________________________________________________________
struct complete
{
std::condition_variable m_cond;
std::mutex m_mutex;
};

As I noted at the outset, I'm no MT expert, so my judgment is suspect, but this
looks like it would work. I like that I can wait for the first result to come
back, and if I don't like it (e.g., because it's an exception), I can easily
continue waiting on the rest of the worker threads. Once I have a result I
like, there a handy list of futures that tells me which workers need to be told
to stop working. (There's no API for that in C++0x, but Chris's solution
augments the promise API, so it's not surprising that we'd have to augment
(i.e., build on) the standard future API, too.)

"Scott Meyers" <> wrote in message
news:i54bdb$n5s$...
> Chris M. Thomasson wrote:
> > ________________________________________________________
[...]
>
> As I noted at the outset, I'm no MT expert, so my judgment is suspect, but
> this looks like it would work. I like that I can wait for the first
> result to come back, and if I don't like it (e.g., because it's an
> exception), I can easily continue waiting on the rest of the worker
> threads. Once I have a result I like, there a handy list of futures that
> tells me which workers need to be told to stop working. (There's no API
> for that in C++0x, but Chris's solution augments the promise API, so it's
> not surprising that we'd have to augment (i.e., build on) the standard
> future API, too.)

Well, the technique should work fine. EXCEPT for the fact that I cannot seem
to find a function that is able to poll a future for "readiness". It appears
that the `future<>::is_ready()' function is nowhere to be found within the
following draft:

"Chris M. Thomasson" <> wrote in message
news:Gcndo.83474$...
[...]
> DAMN! It appears that one has to call a timed wait with a timeout of zero
> in order to perform a poll operation wrt the readiness of a future. Why is
> `future<>::is_ready()' missing in this draft!?

Humm... Perhaps I speak too fast here Scott. I think I can totally get
around the fact that `future<>::is_ready()' does not seem to be included in
the current C++0x draft. It should be simple. Let me think for a moment, and
I will try to post a solution...

On Aug 25, 7:57 pm, Scott Meyers <> wrote:
> James Kanze wrote:
> > Yes. The intent, if I understand it correctly, is to
> > encapsulate the creation of the thread and its join. The
> > results data are set in the thread, and only read after the
> > join in the creating thread.
> This isn't quite true. The calling thread can read the future
> as soon as it's set by the callee (via the corresponding
> promise). The callee thread may then run for a while longer,
> e.g., to invoke destructors of local objects and thread-local
> objects. If the thread runs detached, there may never be
> a join, although that complicates matters at program shutdown.

In other words, it uses a condition in its implementation. (I'm
not sure how this works with exceptions, though.)
> > I'm not as familiar as I'd like to be with the proposed
> > threading in the standard, but I have handled a similar problem
> > with Posix threads: I made the threads detached (so no join was
> > available), and used a (single) condition to communicate, with
> > a table of results, one per thread.
> This sounds like your design had the master thread wait until
> all worker threads were done, i.e., until the table was
> completely filled in. Is that correct?

In my case, yes. In practice, the master thread was woken up
each time a worker thread updated the table. It then checked
whether it had all the information it needed, and went back to
sleep if not.
> My problem is more along the lines of wanting to wake the
> master when any entry in the table is filled in with
> a non-exception result. But that just pushes the notification
> problem into the table object: how is it to know when
> a thread has posted a non-exception result if it doesn't use
> polling?

When the worker thread updates the table, it locks the mutex,
makes the modifications, then called pthread_cond_signal on the
condition and freed the mutex. The master thread then woke up,
and checked the conditions.

"Chris M. Thomasson" <> wrote in message
news:Jfndo.83475$...
> "Chris M. Thomasson" <> wrote in message
> news:Gcndo.83474$...
> [...]
>> DAMN! It appears that one has to call a timed wait with a timeout of zero
>> in order to perform a poll operation wrt the readiness of a future. Why
>> is `future<>::is_ready()' missing in this draft!?
>
> Humm... Perhaps I speak too fast here Scott. I think I can totally get
> around the fact that `future<>::is_ready()' does not seem to be included
> in the current C++0x draft. It should be simple. Let me think for a
> moment, and I will try to post a solution...

Something like this should be able to "work around the problem":
_______________________________________________________
struct complete
{
std::condition_variable m_cond;
std::mutex m_mutex;
};

Humm... I now have to explicitly extend futures and promises in order for
this `multi_wait<>' scheme to work out. Scott, is there truly no way to poll
a `std::future<>' for completion such that a subsequent call to
`std::future<>::get' will be 100% guaranteed to contain either a result or
an exception? What is that `std::future<>::valid' call all about!?

On 8/25/2010 10:46 PM, Chris M. Thomasson wrote:
> DAMN! It appears that one has to call a timed wait with a timeout of zero in
> order to perform a poll operation wrt the readiness of a future. Why is
> `future<>::is_ready()' missing in this draft!?

I don't know that, but what's wrong with doing the wait with a zero timeout?
AFAIK, that's the accepted way to poll.

"Scott Meyers" <> wrote in message
news:i57ham$h7j$...
> On 8/25/2010 10:46 PM, Chris M. Thomasson wrote:
>> DAMN! It appears that one has to call a timed wait with a timeout of zero
>> in
>> order to perform a poll operation wrt the readiness of a future. Why is
>> `future<>::is_ready()' missing in this draft!?
>
> I don't know that, but what's wrong with doing the wait with a zero
> timeout?

A personal bias... :^(

Perhaps the wait function... Ahhhh, I almost tried to optimize a very slow
path...

* Chris M. Thomasson, on 27.08.2010 09:49:
> "Scott Meyers"<> wrote in message
> news:i57ham$h7j$...
>> On 8/25/2010 10:46 PM, Chris M. Thomasson wrote:
>>> DAMN! It appears that one has to call a timed wait with a timeout of zero
>>> in
>>> order to perform a poll operation wrt the readiness of a future. Why is
>>> `future<>::is_ready()' missing in this draft!?
>>
>> I don't know that, but what's wrong with doing the wait with a zero
>> timeout?
>
> A personal bias... :^(
>
> Perhaps the wait function... Ahhhh, I almost tried to optimize a very slow
> path...
>
> :^o
>
>> AFAIK, that's the accepted way to poll.
>
> It works.

g++ 4.5 on Linux ships with <future> (and std::future, std:romise,
std:ackaged_task std::shared_future, std::async are in there). Can't
remember for 4.4, and last time I checked nobody was working on making
<future> work for MinGW.

Usually If the future you got is from std::async and you didn't specify the
launch policy, the invocation of the "asynchronous" function may be deferred
until you call wait or get, in which case your "zero time" wait will have to
wait until the "asynchronous" function (which, in this case, is being invoked
synchronously) returns.

To avoid this possibility, specify a launch policy of std::launch::async when
you call std::async.

(BTW, I don't specify this stuff, I just try to figure out how to explain it.)

"Scott Meyers" <> wrote in message
news:i5cpmc$tmh$...
> On 8/27/2010 12:49 AM, Chris M. Thomasson wrote:
>> "Scott Meyers"<> wrote in message
>>> I don't know that, but what's wrong with doing the wait with a zero
>>> timeout?
> [...]
>>> AFAIK, that's the accepted way to poll.
>>
>> It works.
>
> Usually If the future you got is from std::async and you didn't
> specify the launch policy, the invocation of the "asynchronous" function
> may be deferred until you call wait or get, in which case your "zero time"
> wait will have to wait until the "asynchronous" function (which, in this
> case, is being invoked synchronously) returns.

Humm... That's interesting. I was completely ignorant of that fact! Thanks
for taking the time to mention it Scott.

:^)

> To avoid this possibility, specify a launch policy of std::launch::async
> when you call std::async.

That's definitely good to know!

> (BTW, I don't specify this stuff, I just try to figure out how to explain
> it.)

Share This Page

Welcome to The Coding Forums!

Welcome to the Coding Forums, the place to chat about anything related to programming and coding languages.

Please join our friendly community by clicking the button below - it only takes a few seconds and is totally free. You'll be able to ask questions about coding or chat with the community and help others.
Sign up now!