1

This is a design rather than a code related question. It concerns threadpools, specifically how to organize the execution of the tasks. I am using C++ and Boost thread in a cross platform scenario.

I have groups of tasks that I need to parallelize. X number of groups fire off x number of subtasks. All subtasks must be completed in order for a group to be complete in its turn, but it does not matter in what order the subtasks are completed as long as the group can determine when all subtasks belonging to it are finished. The main thread must wait for all groups to complete in a similar fashion to how a group waits for its subtasks. In other words, it is not important in what order the groups complete as long as the main thread can determine when they're all done.

To put it a different way:

  1. All groups wait for their respective subtasks to finish. It is not important in what order the subtasks finish as long as the group can figure out when they're all completed.

  2. The main thread waits for all groups to complete. It is not important in what order they complete, as long as the main thread can detect when all groups are completed. In other words it is the same exact concept as for the group specific subtasks.

All this must be done with N threads in a pool, plus the main thread so N+1 threads all in total. N must be configurable to any arbitrary value.

If it helps, a task is simply a function that needs to be invoked from one of the N threads.

Does anyone have any tips for how I might implement this?

Philip Bennefall
  • 1,477
  • 5
  • 20
  • 33
  • What are the threads doing? Do they get their own piece of data to work with, or is the data shared? – Krisztián Balla Jul 07 '13 at 16:31
  • The subtasks are independent within the given group, and the groups are independent from other groups. – Philip Bennefall Jul 07 '13 at 16:38
  • To clarify, a task is merely a function that needs to be executed so I guess I would make a queue of them. That queue would have to be threadsafe, and I have to figure out how to determine when all tasks belonging to a certain group are complete and then when all groups are complete as well. – Philip Bennefall Jul 07 '13 at 16:41
  • I would let the tasks on finishing send a signal to the grouping thread and when all tasks are finished the grouping thread sends a signal to the main thread. – Krisztián Balla Jul 07 '13 at 16:47
  • The trouble is that I am limited to N+1 threads, where N might easily be 1 so I would have a maximum of two threads. This is configured by the user of my library. I could therefore not afford to have a special thread to handle group synchronization. – Philip Bennefall Jul 07 '13 at 16:53
  • 1
    Then you could declare one of the working task threads as the "group leader". – Krisztián Balla Jul 07 '13 at 17:03
  • I think I just came up with a possible solution to the problem, which is along similar lines as to what we've been discussing but not exactly the same. I'm going to test it and then if I am successful, submit an answer to my own question for the first time. :D – Philip Bennefall Jul 07 '13 at 17:09
  • If you want a thread pool, and you're already using Boost, I would highly recommend using Boost.Asio. More details here: http://stackoverflow.com/questions/14265676/ – beerboy Jul 08 '13 at 01:23
  • Boost.asio is what I investigated initially. I have used it in other projects with great success. However, it is yet another dependency to introduce and I'm trying to keep that list as short as possible. Boost.thread is already much larger than I could wish. – Philip Bennefall Jul 08 '13 at 08:53

1 Answers1

0

Create two classes:

  • CThreadGroup: Launches a number of threads and waits for them to finish (using join).
  • CMainThread: Launches a series of this class (CThreadGroup) and waits for them to finish (using join). Takes as an inout parameter the number of CThreadGroup to launch and how many subthread CThreadGroup launches

Declare one object of type CMainThread in the main.

Here is the prototype of the classes:

Main thread

#include "ThreadGroup.h"
#include <mutex>
class CMainThread
{
    std::vector<CThreadGroup> vThreadGroups;
    std::vector<std::thread> vThreads;
    const unsigned int nbMainThreads;
    const unsigned int nbSubthreads;

public:
    CMainThread(unsigned int nbMainThreads, 
                unsigned int nbSubthreads);
    ~CMainThread(void);
};


#include <thread>
#include <vector>
class CThreadGroup
{
    const unsigned int nbThreads;
public:
    CThreadGroup(unsigned int nbThreads);
    ~CThreadGroup(void);
};

Thread Group

#include <thread>
#include <vector>
class CThreadGroup
{
    const unsigned int nbThreads;
public:
    CThreadGroup(unsigned int nbThreads);
    ~CThreadGroup(void);
};

Here are the cpp files

CMainThread::CMainThread(unsigned int nbMainThreads_, 
                         unsigned int nbSubthreads_):
nbMainThreads(nbMainThreads_),
nbSubthreads(nbSubthreads_)
{
    std::mutex mut;
    for (unsigned int i=0;i<nbMainThreads;++i)
    {
        vThreads.push_back(std::thread([&]{
            CThreadGroup currThread(nbSubthreads);
            std::lock_guard<std::mutex> lock(mut);
            vThreadGroups.push_back(currThread);
        }));
    }

    for (auto it=vThreads.begin(); it!=vThreads.end(); ++it)
    {
        it->join();
    }
} 

Thread Group

CThreadGroup::CThreadGroup(unsigned int nbThreads_):
nbThreads(nbThreads_)
{
    std::vector<std::thread> vThreads;
    for (unsigned int i=0;i<nbThreads;++i)
    {
        vThreads.push_back(std::thread([i]{std::this_thread::sleep_for(std::chrono::seconds(i));}));
    }

    for (auto it=vThreads.begin(); it!=vThreads.end(); ++it)
    {
        it->join();
    } 
}

Hope that helps,

Gabriel
  • 3,564
  • 1
  • 27
  • 49