0

How can I run a function on a separate thread if a thread is available, assuming that i always want k threads running at the same time at any point?

Here's a pseudo-code

For i = 1 to N
   IF numberOfRunningThreads < k
      // run foo() on another thread 
   ELSE
      // run foo()

In summary, once a thread is finished it notifies the other threads that there's a thread available that any of the other threads can use. I hope the description was clear.

  • 3
    you need a threadpool. – Richard Hodges Apr 10 '18 at 11:40
  • https://stackoverflow.com/questions/19500404/how-to-create-a-thread-pool-using-boost-in-c – HMD Apr 10 '18 at 11:41
  • Yes, but how do I maintain the number of threads that are running? I can create 3 threads at the start, but this approach needs that I queue the job, which will be executed once a thread is available. What I want is to only queue the job only when a thread becomes available, without keeping to check the status but some sort of notification to the other threads that it is now possible to queue a job which will be immediately executed. – Jafar Jamal Apr 10 '18 at 11:48

2 Answers2

0

My personal approach: Just do create the k threads and let them call foo repeatedly. You need some counter, protected against race conditions, that is decremented each time before foo is called by any thread. As soon as the desired number of calls has been performed, the threads will exit one after the other (incomplete/pseudo code):

unsigned int global_counter = n;

void fooRunner()
{
    for(;;)
    {
        {
            std::lock_guard g(global_counter_mutex);
            if(global_counter == 0)
                break;
            --global_counter;
        }
        foo();
    }
}

void runThreads(unsigned int n, unsigned int k)
{
    global_counter = n;
    std::vector<std::thread> threads(std::min(n, k - 1));
    // k - 1: current thread can be reused, too...
    // (provided it has no other tasks to perform)
    for(auto& t : threads)
    {
        t = std::thread(&fooRunner);
    }
    fooRunner();
    for(auto& t : threads)
    {
        t.join();
    }
}

If you have data to pass to foo function, instead of a counter you could use e. g a FIFO or LIFO queue, whatever appears most appropriate for the given use case. Threads then exit as soon as the buffer gets empty; you'd have to prevent the buffer running empty prematurely, though, e. g. by prefilling all the data to be processed before starting the threads.

A variant might be a combination of both: exiting, if the global counter gets 0, waiting for the queue to receive new data e. g. via a condition variable otherwise, and the main thread continuously filling the queue while the threads are already running...

Aconcagua
  • 24,880
  • 4
  • 34
  • 59
0

you can use (std::thread in <thread>) and locks to do what you want, but it seems to me that your code could be simply become parallel using openmp like this.

#pragma omp parallel num_threads(k)

#pragma omp for
for (unsigned i = 0; i < N; ++i)
{
auto t_id = omp_get_thread_num();
if (t_id < K)
   foo()
else
   other_foo()
}
apramc
  • 1,346
  • 1
  • 15
  • 30