I don't understand the why behind it being semantically wrong. I am not
trying have more than one thread in which the joins are called. I am
trying to join all the child threads in the main thread.
> However, your
> copying doesn't work because thread objects are not copyable (copying is
> semantically meaningless and impossible to achieve with POSIX threads
> and probably also windows threads);
I am not trying to "create a new thread by copying the thread object. I
am trying to "pass some thing the main thread can use to join on"
> and returning a pointer won't work
> because it subverts your original intention of making a copy to avoid
> violating the aforementioned requirement.
But it does work in my test. I'll post my edited concept code at the bottom.
> Usually, if you want multiple
> threads to wait for a condition such as thread termination you need to
> use a condition variable.
Just the main thread.
> (Although you do not use C++ threads, the same rule that applies
> to POSIX threads appears to apply to std::thread, because it is a
> precondition of calling join() that the thread should be joinable(),
It should be
> and a thread ceases to be joinable as soon as join() or detach() returns
> in a thread: this is not surprising because the standard states, albeit
> non-normatively, that "These threads [std::thread threads] are intended
> to map one-to-one with operating system threads".)
>
> More generally, your approach seems like an unnecessarily convoluted way
> of starting a thread.
>
> Chris
>
Maybe so. Garnering suggestions on how to edit it are my goal, but I
have a feeling people are misinterpreting what I am trying to do. I just
want to start multiple threads from one parent thread and have that
parent wait on them all to terminate, unless an signal is obtained, in
which case I want the main thread to signal the child threads to
terminate. I am trying to also encapsulate all the
activity/functionality that takes place on the child threads, in a
class. Maybe this concept code does a better job of it:
________
#pragma once
// Boost Includes
#include <boost/shared_ptr.hpp>
#include <boost/thread.hpp>
// Standard Includes
#include <atomic>
//--------------------------------------------------------------------------------------------------
class SomeClass
{
public:
typedef boost::shared_ptr<SomeClass> Shared_Ptr;
static void Start(SomeClass & someClass);
SomeClass();
~SomeClass();
void Stop();
void Join();
protected:
void ThreadProcedure();
static size_t m_count;
size_t m_id;
boost::thread m_thread; // Worker thread this
object will run on
boost::function<void()> m_threadProcedure; // Function object that
is the main loop for the thread
std::atomic<bool> m_stopping; // Flag to exit loop in
thread
};
__________
#include "SomeClass.h"
// Boost Includes
#include <boost/thread.hpp>
// Standard Includes
#include <iostream>
//--------------------------------------------------------------------------------------------------
size_t SomeClass::m_count = 0;
//--------------------------------------------------------------------------------------------------
void SomeClass::Start(SomeClass & someClass)
{
// Start the thread
someClass.m_thread.swap(boost::thread(someClass.m_threadProcedure));
}
//--------------------------------------------------------------------------------------------------
void SomeClass::Stop()
{
// Flags the loop in the thread procedure to exit
m_stopping = true;
}
//--------------------------------------------------------------------------------------------------
void SomeClass::Join()
{
m_thread.join();
}
//--------------------------------------------------------------------------------------------------
SomeClass::SomeClass()
:
m_id (m_count++),
m_thread (),
// Derived classes should override this behavior by binding their
own thread procedure in their constructor
m_threadProcedure(boost::bind(&SomeClass::ThreadProcedure, this)),
m_stopping (false)
{
std::cout << "SomeClass #" << m_id << " constructor has been
called." << std::endl;
}
//--------------------------------------------------------------------------------------------------
SomeClass::~SomeClass()
{
std::cout << "SomeClass #" << m_id << " deconstructor has been
called." << std::endl;
// If the thread is still running, interupt it and wait for it to join
m_thread.interrupt();
m_thread.join();
}
//--------------------------------------------------------------------------------------------------
void SomeClass::ThreadProcedure()
{
std::cout << "SomeClass #" << m_id << " thread procedure has been
called." << std::endl;
// Will go forever until interupted
try
{
while(!m_stopping.load())
{
boost::this_thread::sleep_for(boost::chrono::seconds(60));
std::cout << "SomeClass #" << m_id << " tick..." << std::endl;
}
}
catch(boost::thread_interrupted)
{
std::cout << "SomeClass #" << m_id << " interupted. Exiting
thread." << std::endl;
}
std::cout << "SomeClass #" << m_id << " stopped. Exiting thread."
<< std::endl;
}
____________
// Project Includes
#include "SomeClass.h"
// Shared Library
#include "Exception.h"
#include "PerformanceTimer.h"
// Boost Includes
#include <boost/thread/thread.hpp>
// Standard Library
#include <cstdio>
#include <iostream>
#include <string>
#include <vector>
#include <cmath>
#include <signal.h>
//--------------------------------------------------------------------------------------------------
// TODO - Only global for concept testing, later we'll wrap all this in
a class
std::vector<SomeClass::Shared_Ptr> g_workerObjects;
//--------------------------------------------------------------------------------------------------
void SignalHandler(int signal)
{
// Signal worker thread to exit
std::cout << "Halt signal caught. Exiting all threads..." << std::endl;
for(std::vector<SomeClass::Shared_Ptr>::const_iterator itWorker =
g_workerObjects.begin(); itWorker != g_workerObjects.end(); ++itWorker)
{
(*itWorker)->Stop();
}
}
//--------------------------------------------------------------------------------------------------
int main()
{
// Register a handler for the ctrl-z signal
signal(SIGINT, SignalHandler);
// Create the workers and start thier threads
for(size_t index = 0; index < 10; ++index)
{
SomeClass::Shared_Ptr worker(new SomeClass());
SomeClass::Start(*worker);
g_workerObjects.push_back(worker);
}
// Wait for the threads to exit
std::vector<SomeClass::Shared_Ptr>::const_iterator itWorker =
g_workerObjects.begin();
while(itWorker != g_workerObjects.end())
{
(*itWorker)->Join();
itWorker = g_workerObjects.erase(itWorker);
}
// Done
return 0;