I have got Worker
classes and a Handler
class to create an abstraction layer for jobs already. I wanted to use std::async
to pour some asynchrony into the mix but I got some weird behaviour from my Visual Studio 2012 (update 1).
My class hierarchy is as follows:
Worker
is an abstract class withInit
andWork
as pure virtual methods.BasicWorker : Worker
is simply usingprintf
for some output.GroupWorker : Worker
is an aggregation of other workers.Handler
holds on to aWorker
to do some job.
Then I call several std::async
methods in which I create workers and a handler, I call for handler in a nested std::async
call and, I wait for initialization (std::condition_variable
here) of the worker and then I stop the handler.
In the end I wait for all of the std::future
s to finish.
The code is as follows:
#include <stdio.h>
#include <future>
#include <array>
#include <atomic>
#include <vector>
struct Worker
{
virtual ~Worker() { }
virtual void Init() = 0;
virtual void Work() = 0;
};
struct BasicWorker : public Worker
{
virtual ~BasicWorker() { }
virtual void Init()
{
printf("\t\t\t\tInit: %d\n", std::this_thread::get_id());
}
virtual void Work()
{
printf("\t\t\t\tWork: %d\n", std::this_thread::get_id());
}
};
struct GroupWorker : public Worker
{
GroupWorker()
{
workers.push_back(std::make_shared<BasicWorker>());
}
virtual ~GroupWorker() { }
virtual void Init()
{
for(int i = 0; i < workers.size(); ++i)
{
workers[i]->Init();
}
initEvent.notify_all();
}
virtual void Work()
{
for(int i = 0; i < workers.size(); ++i)
{
workers[i]->Work();
}
}
void WaitForInit()
{
//std::unique_lock<std::mutex> initLock(initMutex);
//initEvent.wait(initLock);
}
private:
std::mutex initMutex;
std::condition_variable initEvent;
std::vector<std::shared_ptr<Worker>> workers;
};
struct Handler
{
static const int Stopped = -1;
static const int Ready = 0;
static const int Running = 1;
Handler(const std::shared_ptr<Worker>& worker) :
worker(worker)
{ }
void Start(int count)
{
int readyValue = Ready;
if(working.compare_exchange_strong(readyValue, Running))
{
worker->Init();
for(int i = 0; i < count && working == Running; ++i)
{
worker->Work();
}
}
}
void Stop()
{
working = Stopped;
}
private:
std::atomic<int> working;
std::shared_ptr<Worker> worker;
};
std::future<void> Start(int jobIndex, int runCount)
{
//printf("Start: %d\n", jobIndex);
return std::async(std::launch::async, [=]()
{
printf("Async: %d\n", jobIndex);
auto worker = std::make_shared<GroupWorker>();
auto handler = std::make_shared<Handler>(worker);
auto result = std::async(std::launch:async, [=]()
{
printf("Nested async: %d\n", jobIndex);
handler->Start(runCount);
});
worker->WaitForInit();
handler->Stop();
result.get();
});
}
int main()
{
const int JobCount = 300;
const int RunCount = 5;
std::array<std::future<void>, JobCount> jobs;
for(int i = 0; i < JobCount; ++i)
{
jobs[i] = Start(i, RunCount);
}
for(int i = 0; i < JobCount; ++i)
{
jobs[i].get();
}
}
My problem is:
- If I uncomment the lines in
WaitForInit@GroupWorker
function then my nested asynchronous function calls are not made until all first level asynchronous function calls are made - While waiting
std::condition_variable
if I increase the number of jobs, creation of new threads feels like getting exponentially slow. For my trial below 100 jobs there exist some asynchrony but above 300 it is completely sequential to create jobs. - Then if I uncomment the
printf
line inStart
method, all nested asynchrony works like a charm
So,
- What am I doing wrong in usage of
std::condition_variable
? - Why creating jobs gets slower for like 100s of threads? (this question is optional, seems like a problem of OS and can be fixed with a smart thread-pool concept)
- What has
printf
to do with any of this? (I tried deleting allprintf
calls in the case of a race condition and I put a breakpoint in the code but no help. It is the same case withstd::cout
too)
Edit:
I have added launch policy (as suggested by Jonathan Wakely) for assuring the creation of a thread. But that did not help either. I am currently creating a std::thread
and calling thread::join
function for waiting inside the first level async.