Boost logo

Boost :

From: Beman Dawes (bdawes_at_[hidden])
Date: 2001-06-27 10:33:51


At 10:03 AM 6/27/2001, williamkempf_at_[hidden] wrote:

>> >The first impediment is that it's a very common practice to copy
>> >the "thread" (actually the descriptor, as you pointed out) in
>order
>> >to allow some other piece of the program manage the thread. For
>> >instance, a thread pool is going to have to store the threads in
>some
>> >sort of data structure. This design requires all such uses to be
>> >dynamically allocated via new, which complicates the management
>> >though it can be lessened by immediately placing them in a smart
>> >pointer.
>>
>> This just seems like the usual thing you do in C++; that's what
>smart
>> pointers are for.
>
>The difference is, most types that are noncopyable rarely need to be
>copied. Scope lifetime is normal for them.

The production library I make my living from manages all files dynamically
because bindings are done at runtime. So files are managed by smart
pointers. It really isn't a problem.

>This type isn't the
>same. The norm is for them to be copied and passed out of scope. So
>you nearly always must use the smart pointer, which is tedious and
>repetitious. If the functionality can be encapsulated to reduce
>this, I generally think that you should.

Yes, I understand that many of the old time thread programmers are
certainly going to want the encapsulation to be focused on the
identifier. But what about those of us who prefer to deal with an object
interface? Should we just roll our own thread class?

>> >The second impediment is that constructing threads in a loop
>becomes
>> >much more complicated. You either have to (again) create the
>threads
>> >dynamically via new and manage the lifetime (probably with a smart
>> >pointer),
>>
>> Wouldn't shared_ptr<> work just fine? Am I missing something?
>
>It works (and is what I suggested would be done), but again, it's
>tedious and repetitious, especially given the frequency of this idiom
>in code.

But I do that all the time. I'm used to shared_ptr<>. I understand it. I
don't have to figure out the semantics of an unknown reference counted
pointer.

>> > or call detach() on all of them. The detach() approach
>> >will either require you to not care when the threads end (in fact,
>> >they will be unceremoniously terminated when the main thread
>exits),
>> >or to have join_all() join even detached threads (the latter means
>> >that you can only use the detach() method if you know what threads
>> >may be running and want to wait on all of them).
>>
>> All that seems appropriate for some applications? Again I'm
>missing why
>> this is a problem.
>
>You will rarely no all of the threads running in your application.
>For example, a GUI widget that displays an MPEG video may spawn a
>thread to display the frames. As the application programmer I
>wouldn't be aware of this thread, typically, and I certainly wouldn't
>want join_all() to wait on this thread, since it would likely cause a
>deadlock.

I'm sorry, I lost the thread (pun intended) of what you were trying to do.

>
>> >With smart pointers you get the same result here as you do with
>the
>> >thread_desc concept, but the syntax becomes more complicated,
>which
>> >is going to be a hard sell for some developers.
>>
>> But you only use a smart pointer when you need more than function
>scope
>> management, or maybe a variable number. If you had an app that
>just used
>> two threads, wouldn't you would just instantiate the two threads,
>like any
>> other object? I think of it like files; if you know you need an
>input file
>> and an output file you just code something like:
>>
>> ifstream input("foo.in");
>> ofstream output("foo.out");
>
>Threads are not totally like files in this regard. Rarely will
>a "thread of execution" (boy I see why you hate the name thread for
>the type) be bound to some scope in the creating thread. Again,
>think of the MPEG widget above. At first you might think that the
>thread's scope being defined by the lifetime of the widget, but the
>widget may have start and stop buttons that define the lifetime of
>the thread. Rarely will a thread's lifetime fit nicely into a simple
>block scope.

Just like files in production code, where you don't know how many other
components still have the same file open, so you use a smart pointer to
manage them.

>
>> However, if their lifetime should be different than function scope,
>or you
>> need a variable number of files, then you use a smart point. But
>only
>> then.
>
>The scales differ here, however. Pulling numbers out of thin air
>just for demonstration, if files need something other than function
>scope 10% of the time, threads need it 90% of the time. This puts a
>lot more burden on the user. If the burden can be encapsulated and
>taken care of for him, I believe you should do so.

Ah! That's a real difference between our experience. In my critical
library, 100% of the files are disconnected from function scope. Of course
for lots of other code the percent is much lower, but still I'm very used
to having to keep track of files (and similar objects like B-trees) with
smart pointers.

>Another thing I've thought of over night. This design makes it
>impossible to have a self() method, but I think this functionality is
>a necessity.

Yes, you said that before, but I still don't understand why you don't just
pass a thread ref or pointer to the initial thread function as an
argument. Why isn't that satisfactory?

--Beman


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk