[cpp-threads] Re: Thread API interface tweaks
Peter Dimov
pdimov at mmltd.net
Wed Aug 30 21:13:05 BST 2006
Howard Hinnant wrote:
> I'm about 24 hours behind in this thread, my apologies. I'm posting
> back in part just to catch up and confirm my understanding...
>
> On Aug 28, 2006, at 1:59 PM, Peter Dimov wrote:
>> Here's my future<> sketchy implementation for reference:
>
> Thanks Peter, that really helped. I'm beginning to understand (I'm a
> slow learner :-)). Here future<R> is truly a *non-intrusive* wrapper
> around thread<void>, unlike the partially intrusive wrapper I laid
> out. I really like that it is completely non-intrusive. This means
> it is not possible for it to impact the lower level thread launching
> efficiency.
>
> I don't think your design specifies that thread can't return a type.
> It only specifies that future<R> builds on thread<void> instead of
> thread<R> like I had. So *if* we want thread to be able to return
> non-void types (and I strongly suspect we might), we can still do
> that with your design.
In principle, yes, we can. future<R> builds on anything that can execute
asynchronously, and it will be usable with thread<void>, should we decide to
have one.
But the other part of my design that I haven't shown yet does specify that
threads don't return values and represent simply threads of
control/execution.
I was actually a proponent of thread<R> once, so it's not a NIH syndrome; I
just realized that the motivation for having thread<R> is simply that I was
missing the future<R> component.
My current proposal for threading API is:
namespace thread
{
class handle;
// DefaultConstructible (NULL), CopyConstructible, Assignable
// EqualityComparable
// LessThanComparable, Hashable
// OutputStreamable (for diagnostic purposes)
template<class F> handle create( F f );
void join( handle th );
// pre: th != 0
// effects: blocks
// post: the thread identified by th has ended
// throws: cancellation exception
bool try_join( handle th );
bool timed_join( handle th, timespec const & tm ); // or boost::xtime, or
whatever
handle current();
// Cancellation still under discussion, controversial
//
// void cancel( handle th );
// void set_cancel_state( handle th, bool st );
// void test_cancel();
// Yield/sleep do not belong to a high-level thread API, but there's popular
demand...
//
// void yield();
// void sleep( timespec const & tm );
}
As an example in favor of separating return values and threads, consider the
case of a thread pool. On startup, it creates (say) eight threads. When a
task is submitted, a free thread is assigned to this task and a future is
returned to the caller (see pooled_executor in my first post.)
The key here is that you don't know the return type in advance; the same
thread can be used to execute tasks with different return values.
> Some downsides though:
>
> The syntax for launching a thread, and launching a future are
> different, e.g.:
>
> thread<int> t = launch_thread(g);
>
> future<int> f = threaded_executor().execute(g);
>
> The above is probably more different than it needs to be, but still,
> with Peter's (I think superior) proposal, at the very least we are
> looking at:
>
> thread<int> t = launch_thread(g);
> future<int> f = launch_future(g);
I just want to add here that the syntactic complexity in
> future<int> f = threaded_executor().execute(g);
is caused by the fact that threaded_executor conforms to the Executor
concept. In practice, one would probably use
future<int> f = ex.execute(g);
where ex would model Executor but not be hardcoded as a threaded_executor().
The Executor concept corresponds to Kevlin's threader, with the difference
that its models are not primitives, but can be implemented on top of the
supplied primitives.
When one just needs a background thread per task, the syntax is indeed
somewhat clumsy. We can wrap it with a convenience function, as you state
above:
> future<int> f = launch_future(g);
but the caller will usually be able to choose a more efficient Executor
based on g's level of parallelism.
Of course if we can implement a semi-magical function that will outperform
70% of people's hand-coded thread pools, we ought to provide it:
future<int> f = active( g );
;-)
More information about the cpp-threads
mailing list