1

I need to spawn a thread when a certain trigger event is received inside of a class Foo. The trigger event is received by a Winsock server class that has a reference to the variable triggerEvent.

bool Foo::HandleEvents()
{
    while (1)
    {
        // Other things are done at the top of this loop

        switch (triggerEvent)
        {
            case 'h':
            {
                // I instantiate an object here to do 
                // what I need to do in the thread.
                // I use a pointer that is a private
                // member of Foo.
                thingMaker = new ThingMaker(params);

                // Spawn a new thread here calling a
                // function of ThingMaker and using thingMaker
                break;
            }
            case ...: return true;
            default: break;
        }
    }
}

Since the thread is local to its case in the switch, I lose access to it on break. I can't call join() because I'm dealing with real-time processing and cannot wait for the thread to finish unless I know it's already done.

I recently asked a question about threading here regarding the same application and was told detach() is bad practice; I also think my question was too vague as the solution offered ended up not fitting my needs and my application has since changed in architecture.

I have also tried to encapsulate the thread in short-life manager class that creates instances of ThingMaker but to no avail.

How do I go about this? I suspect my main issue is scope, but my options are limited. Foo::HandleEvents() cannot be delayed at all or else I lose critical data.

Community
  • 1
  • 1
  • 3
    Have you considered a system where you *do* control the thread lifetimes, such as a thread-pool where the posted code posts your `params` to a queue monitored by the pool for work to be dispatched? – WhozCraig Jul 06 '16 at 15:34
  • How about dealing with `futures` ? Do you have C++11 ? – Arunmu Jul 06 '16 at 15:35
  • 1
    Why not make the `thread` a member of the `Foo` class then you always have access to it? A question I have is what do you want to happen if the thread is still executing when `HandleEvent` function is triggered? You may need data member to track all the outstanding threads ... – James Adkison Jul 06 '16 at 15:36
  • @WhozCraig I did try a vector of threads that was declared before the `while(1)` loop, and each time `triggerEvent` was 'h', I added a thread to the vector. When the termination event trigger was received, I iterated through the vector and called `join()` on each thread. The issue with a proper thread pool is that the thread needs to end - the thread function returns. – network drift Jul 06 '16 at 15:47
  • @JamesAdkison `HandleEvents` is the driving function of the application. It's running until the application is terminated. – network drift Jul 06 '16 at 15:48
  • @cjclough That's not a thread pool. In its simplest form, a set of threads are started, all of them waiting on the queue for work. A thread pool fundamentally is setup as a (usually, but not always fixed) collection of threads that (a) monitor a shared workload resource such as a queue, and (b) upon receiving data from that queue, invoke a work function to process the item just-dequeued. Once finished, the thread returns back to the queue for more work (where it may-well wait if there is none to dispatch). it is much more involved than simply declaring a vector of threads. – WhozCraig Jul 06 '16 at 15:52
  • @WhozCraig Ah, I understand now, thank you. I think this might work. My only concern is that my "item" is a series of files saved to disk, the range of which is determined and changeable by the end user. – network drift Jul 06 '16 at 16:08
  • @cjclough Instead of using a break statement, try using a goto statement. That may allow you to continue access to the thread. –  Jul 06 '16 at 19:16
  • @code0 `goto` is terrible practice, and `switch` cases have their own scope. – network drift Jul 06 '16 at 19:45
  • @cjclough If used wisely and infrequently (to know exactly where it is in the program in case problems arise), it is a simple and easy way to jump around a program efficiently. I like this explanation: https://www.researchgate.net/post/What_is_the_best_alternatives_for_goto_statements_in_C As for the switch statement scope problem, why not use if-else statements? Just using this might solve your problem because its avoiding the local scope of the switch statement. –  Jul 06 '16 at 20:13
  • @code0 Point taken regarding `goto`, but if-else statements have their own scope as well. You cannot access a thread declared in `if (triggerEvent == 'h')` outside of that block. – network drift Jul 06 '16 at 20:33
  • @cjclough That's a piece of information that I haven't learned at all in the time I've been programming. Good to know. –  Jul 06 '16 at 23:10

1 Answers1

1

You could use a std::map (or one of the other similar containers):

class Foo
{
    bool HandleEvents();
    std::map<ThingMaker*, std::thread> m_map;
};

bool Foo::HandleEvents()
{
    while (1)
    {
        switch (triggerEvent)
        {
            case 'h':
            {
                thingMaker = new ThingMaker(params);
                m_map[thingMaker] = std::thread(function_ptr, thingMaker);
            } break;
            case 't': // termination event trigger
            {
                m_map[thingMaker].second.join();
                m_map.erase(thingMaker);
                delete thingMaker;
            } break;
            case ...: return true;
            default: break;
        }
    }
}

Since this obviously isn't your full code you'd have to adjust the above code to fit your needs, but you could swap the map's key/value in the template, or use the thread ID instead if that would make more sense (e.g. std::map<std::thread::id, ThingMaker*>, etc.), but something like a map avoids iterating over an array and joining on each thread or having to implement a full thread pool implementation if you don't necessarily need one.

Side note: the use of detach is not bad, in fact it's quite useful; detaching a thread signals to the kernel that the thread can be "cleaned up" as soon as it's done executing (which releases certain resources and handles). Calling detach on a thread is useful when you know you will no longer need access to the underlying thread handle (like in an extremely short lived thread). It's neither bad nor good, simply a tool to utilize (like the infamous goto statement).

Hope that can help.

txtechhelp
  • 6,625
  • 1
  • 30
  • 39