
Peter Dimov wrote:
Yuval Ronen wrote:
[...]
If we agree that threads cannot be copied,
We don't. More details below.
Sorry for jumping in between. But I always was wondering what "copying a thread" could mean. Innocently I would expect creating a second instance of the thread similar to a "fork" of processes in unix. But I don't think this is what you have in mind. Otherwise copying the whole state of a thread is not of much sense either (thread needs to be scheduled.) I think noncopyable is a property of a thread, and the design simply refelcts this fact, but I might be wrong in this respect and would like to hear what I am missing.
it means that each thread can be represented by only one thread object. Pass the address of thread objects and compare them, if you really want to. It'll give you the exact same result.
The problem with your reasoning is that the thread object may have been destroyed. Its lifetime is not tied to the lifetime of the thread.
This is true. But where is the problem? Once I have deleted the thread object (not the thread!) I am not able to compare it to anything, since the memory is invalid, isn't it? As I see it, the demand for thread ID's or references mainly come from the attempt to communicate with the thread. (Are there really other usages?) So not having a thread ID is no drawback at all, since this problem can be solved easily by other means. I append a small example showing how a thread can communicate with another, using a control object with automatic lifetime: Some remarks 1) from inside the thread I use a tls that holds the communication object 2) from outside I use a wrapper object 3) control of lifetime of this object is tricky, since it must last as long as both communicating threads have agreed to dipose it. #include <boost/thread.hpp> #include <boost/bind.hpp> struct control_impl; boost::thread_specific_ptr<control_impl*> g_control; struct control_impl { control_impl() : stopped(false), ref(1) {} void stop() { boost::mutex::scoped_lock lk(monitor); stopped = true; control_changed.notify_one(); } void wait_until_stopped() { boost::mutex::scoped_lock lk(monitor); while(!stopped) control_changed.wait(lk); } void release() { boost::mutex::scoped_lock lk(monitor); if (--ref == 0) { lk.unlock(); delete this; g_control.reset(0); } } void addref() { boost::mutex::scoped_lock lk(monitor); ++ref; } boost::mutex monitor; boost::condition control_changed; bool stopped; int ref; }; // the control struct to be used from outside the thread struct control { control() { pimpl = new control_impl; } ~control() { pimpl->release(); } void stop() {pimpl->stop(); } control_impl* pimpl; }; // the functions to be used from inside the thread void wait_until_stopped() { (*g_control)->wait_until_stopped(); } void register_control(control* p) { p->pimpl->addref(); g_control.reset(new control_impl*(p->pimpl)); } void release_control() { (*g_control)->release(); } // the user program void foo() { wait_until_stopped(); } void run(control* p) { register_control(p); foo(); release_control(); } int main(int argc, char* argv[]) { control* pc = new control; boost::thread* pt = new boost::thread(boost::bind(run,pc)); pc->stop(); delete pc; pt->join(); return 0; } In a real implementation of course the user code should not have access to the control_impl, so it cannot mess with the reference counting. I admit however that the real question remains: What is a thread object then at all? At the momnet its only purpose is to serve as an access point for join. I think it s worth about thinking about a generalized mechansim that will allow access of user defined structures from inside and outside a thread. Roland