I just created my Thread Pool for game server, but i got one error in compiling what i didn't know how to fix.
Error :
Connection/CConnection.cpp: In lambda function:
Connection/CConnection.cpp:62:6: error: 'this' was not captured for
this lambda function
Thread Pool declaration :
class Worker {
public:
Worker(ThreadPool &s) : pool(s) { }
void operator()();
private:
ThreadPool &pool;
};
// the actual thread pool
class ThreadPool {
public:
ThreadPool(size_t);
template<class F>
void enqueue(F f);
~ThreadPool();
private:
// need to keep track of threads so we can join them
std::vector< std::unique_ptr<boost::thread> > workers;
// the io_service we are wrapping
boost::asio::io_service service;
boost::asio::io_service::work working;
friend class Worker;
};
template<class F>
void ThreadPool::enqueue(F f)
{
service.post(f);
}
Function what use it :
void CConnection::handle()
{
int i = 0;
ThreadPool pool(4);
pool.enqueue([i]
{
char * databuffer;
databuffer = new char[16];
for(int i = 0;i<16;i++)
{
databuffer[i] = 0x00;
}
databuffer[0] = 16;
databuffer[4] = 1;
databuffer[8] = 1;
databuffer[12] = 1;
asynchronousSend(databuffer, 16);
});
}
Can someone tell me where, and what is problem ?
My guess is that asynchronousSend is a function in the CConnection class. To call function in object you have to capture this:
pool.enqueue([this] { ... });
As you see I've removed the capture of i as it's not needed, since you declare a local i inside the lambda.
Related
My program has a class similar to the following:
class IOWorker {
std::thread thread_;
boost::asio::io_service ios_;
boost::optional<boost::asio::io_service::work> work_;
Callback callback_;
static thread_local IOWorker* this_thread_worker_;
public:
IOWorker(Callback cb);
~IOWorker();
// IO worker threads will call this to fetch their instance
static IOWorker* getIOWorkerInstance (void) {
return this_thread_worker_;
}
};
IOWorker::IOWorker (Callback cb) : callback_{std::move(cb)}
{
work_ = boost::in_place(boost::ref(ios_));
thread_ = std::thread{[this] () {
this_thread_worker_ = this;
ios_.run();
}};
}
Main thread calls the following function to create IOWorker instances/threads:
std::vector<IOWorker> startIOWorkers(Callback cb)
{
std::vector<IOWorker> launched_workers;
launched_workers.reserve(10);
for (int i = 0; i < 10; ++i) {
launched_workers.emplace_back(cb);
}
return launched_workers;
}
But the compilation fails with the following error (pointing to _Up):
error: call to implicitly-deleted copy constructor of 'IOWorker'
::new((void*)__p) _Up(_VSTD::forward<_Args>(__args)...);
Not sure what changes I need to make in the above class to resolve this error.
I want this class (objects) to be non-copyable, but can be movable.
I have a class:
class IOWorker {
std::thread thread_;
boost::asio::io_service ios_;
boost::optional<boost::asio::io_service::work> work_;
Callback callback_;
// Map of thread::id and this class' instance
typedef std::map<std::thread::id, IOWorker *> IOWorkerThreadMap;
static IOWorkerThreadMap iOWorkerThreadMap;
public:
IOWorker(Callback cb);
~IOWorker();
std::thread::id getThreadId() {
return thread_.get_id();
}
// IO worker threads will call this to fetch their instance
static IOWorker* getIOWorkerInstance (void) {
auto it = iOWorkerThreadMap.find(std::this_thread::get_id());
if (it == iOWorkerThreadMap.end()) {
return nullptr;
}
return (it->second);
}
};
IOWorker::IOWorker (Callback cb) : callback_{cb}
{
work_ = boost::in_place(boost::ref(ios_));
thread_ = std::thread{[this] () {
ios_.run();
}
};
}
In a function executed by the main thread, I'm creating 10 instances of this class and inserting these into the map where thread::id is key and class instance is value.
I'm accessing this map from all the worker threads to fetch their respective class instance by looking up their thread::id in the map. The main thread accesses these instances too, to call some methods, post jobs on ios_, etc.
void startIOWorkers (Callback cb)
{
for (int i = 0; i < 10; ++i) {
IOWorker *iow = new IOWorker{cb};
std::thread::id threadId = iow->getThreadId();
IOWorkerThreadMap.insert(std::make_pair(threadId, iow));
}
}
My question is for below line:
IOWorkerThreadMap.insert(std::make_pair(threadId, iow));
My understanding (could be wrong!) is that iow and threadId in above function, will be "copied" while I'm inserting them in the map and two copies of them will exist.
I want to avoid that, therefore, I would like to know what are the better ways of having a map of thread::id and class instance in this case?
This seems way more complicated than it has to be.
If, as it appears to be the case, you only need to access the map at from thread::this_thread, the language already has a map<thread_id, T> built right in: thread_local.
class IOWorker {
std::thread thread_;
boost::asio::io_service ios_;
boost::optional<boost::asio::io_service::work> work_;
Callback callback_;
static thread_local IOWorker* this_thread_worker_;
public:
IOWorker(Callback cb);
~IOWorker();
// IO worker threads will call this to fetch their instance
static IOWorker* getIOWorkerInstance (void) {
return this_thread_worker_;
}
};
IOWorker::IOWorker (Callback cb) : callback_{std::move(cb)}
{
work_ = boost::in_place(boost::ref(ios_));
thread_ = std::thread{[this] () {
this_thread_worker_ = this;
ios_.run();
};
}
std::vector<IOWorker> startIOWorkers(Callback cb)
{
std::vector<IOWorker> launched_workers;
launched_workers.reserve(10);
for (int i = 0; i < 10; ++i) {
launched_workers.emplace_back(cb);
}
return launched_workers;
}
I am trying to create a ThreadPool class which has a constructor like this
ThreadPool(size_t numberOfThreads)
: workerThreads(numberOfThreads) {
workerThreads.reserve(numberOfThreads);
for(int i =0; i < numberOfThreads; i++) {
workerThreads.emplace_back(std::thread(&ThreadPool::doJob, this));
}
}
This fails to compile for me and throws following error
error: attempt to use a deleted function
__invoke(_VSTD::move(_VSTD::get<1>(__t)), _VSTD::move(_VSTD::get<_Indices>(__t))...);
^
/Library/Developer/CommandLineTools/usr/include/c++/v1/thread:352:5: note: in instantiation of function template specialization
'std::__1::__thread_execute<std::__1::unique_ptr<std::__1::__thread_struct, std::__1::default_delete<std::__1::__thread_struct> >, void
(ThreadPool::*)(std::__1::function<void (char *, std::__1::list<std::__1::basic_string<char>,
std::__1::allocator<std::__1::basic_string<char> > > *)>), ThreadPool *, 2>' requested here
__thread_execute(*__p, _Index());
As per other answers to similar question I also tried doing this
workerThreads.emplace_back(std::thread(&ThreadPool::doJob, std::ref(*this)));
This is reproducing same issue as well. I am compiling my code with clang c++14 on macos
Here is complete program where this reproduces
class ThreadPool {
public:
ThreadPool(size_t numberOfThreads)
: workerThreads(numberOfThreads) {
for(int i =0; i < numberOfThreads; i++) {
workerThreads[i] = std::make_shared<std::thread>(std::thread(&ThreadPool::doJob, this));
}
}
ThreadPool(const ThreadPool& tp) {
workerThreads = tp.workerThreads;
jobQueue = tp.jobQueue;
}
std::function<void(char*, std::list<std::string>*)> getNextJob() {
if(!jobQueue.empty()) {
std::function<void(char*, std::list<std::string>*)> job = jobQueue.front();
jobQueue.pop_front();
return job;
}
throw std::runtime_error("No jobs to process, thread finished");
}
void addWork(std::function<void(char*, std::list<std::string>*)> job) {
lockListMutex.lock();
jobQueue.push_back(job);
lockListMutex.unlock();
}
private:
// performs actual work
void doJob(std::function<void(char*, std::list<std::string>*)> job) {
try {
lockListMutex.lock();
getNextJob();
lockListMutex.unlock();
} catch (std::runtime_error &e) {
}
}
// a vector containing worker threads
std::vector<std::shared_ptr<std::thread>> workerThreads;
// a queue for jobs
std::list<std::function<void(char*, std::list<std::string>*)>> jobQueue;
// a mutex for synchronized insertion and deletion from list
std::mutex lockListMutex;
};
int main(int argc, char *argv[]) {
int numThreads = 1;
ThreadPool* pool = new ThreadPool(numThreads);
}
Since your doJob member function has a parameter job of type std::function<void(char*, std::list<std::string>*)>, you need to provide a corresponding argument when this function is called. Therefore, calling of this function in a new thread this way is invalid:
std::thread(&ThreadPool::doJob, this) // no argument for job passed
You either need to provide an argument, or, since the jobs should be likely dequeued from jobQueue, remove the job parameter from doJob:
void doJob()
{
... // dequeue job from jobQueue and execute it
I need to spawn class object. For example:
class Worker {
Worker(int arg1, Object *obj);
void workLoop() { while(true) { ... } }
}
And I need spawn in loop threads with creating objects. When I do this "static", it works:
thread th1(&Worker::workLoop, Worker(args...));
thread th2(&Worker::workLoop, Worker(other args...));
But I can't spawn this in loop. I tried:
for (...) {
thread th(&Worker::workLoop, Worker(...));
threadsVector.push_back(std::move(th));
}
...but only first thread works.
Also, I have in Worker class this:
std::thread spawn() {
return std::thread(&Worker::workLoop, this);
}
I don't know how do this and why loop can't spawn my threads correctly.
try this
class Worker{
Worker(int arg, Object *obj)
void workLoop() { while(true) { ... } }
}; // do not forget the semicolon
....
vector<thread> pool;
auto func = [](Worker w){
w.workLoop();
};
// example with thread
for (int i = 0; i < 5; ++i)
pool.push_back(thread(func, Worker(5, obj)));
for (int i = 0; i < pool.size(); ++i)
pool[i].join();
// example
create a lambda expression that takes in a worker object and calls the workLoop method inside
then you can pass the lambda as an object and pass it an argument inside the thread constructor
I need to make a class method take a template function as a pointer to function parameter, and then call it.
But I am running into
ThreadPool.hh:55:2: error: no matching function for call to ‘ThreadPoolElem<int>::start(<unresolved overloaded function type>, ThreadPoolElem<int>*)’
Samples and explanation of my code are :
I have a main module ThreadPool, which have a vector of ThreadPoolElem. As you may have guessed ThreadPoolElem are an encapsulation of Threads and ThreadPool plays the thread pool manager role. From ThreadPool I need to iterate the threadPoolElem vector. For each element in the vector I need to call its 'start' member function which take 2 argumens (the first one being a pointer to function). In the 'start' member function of The ThreadPoolElem class it uses the first argument as the callback for the third pthread_create c library function.
my threadpool file goest like this :
template <typename T>
class ThreadPool
{
Mutex _mutex;
CondVar _condVar;
SafeQueue<T> _tasks;
std::vector<ThreadPoolElem<T> > _threadVector;
/* .... */
void run(void)
{
for (typename std::vector<ThreadPoolElem<T> >::iterator it = this>_threadVector.begin();
it != this->_threadVector.end();
++it)
{
it->start(run_tramp, &(*it));
}
}
My ThreadPoolElem file goes like this:
template <typename T>
class ThreadPoolElem : public ThreadT<T>
{
SafeQueue<T> *_tasks;
CondVar *_condVar;
Mutex *_mutex;
public:
ThreadPoolElem(void) {}
void init(SafeQueue<T> *queue, CondVar *condVar, Mutex *mutex){
this->_mutex = mutex;
this->_condVar = condVar;
this->_tasks = queue;
}
void run(void){
while (true)
{
this->_mutex.lock();
if (this->_tasks.empty())
{
this->_condVar->wait();
this->_mutex->unlock();
}
else
{
int value;
this->_tasks->tryPop(&value);
std::cout << "consuming value" << std::endl;
if (!this->_tasks->empty())
{
this->_mutex->unlock();
this->_condVar->signal();
}
else
this->_mutex->unlock();
}
}
}
};
This is c like function (trampoline function) i need to take as a ptr to function parameter
template <typename T>
void *run_tramp(void *data)
{
reinterpret_cast<ThreadPoolElem<T> *>(data)->run();
return NULL;
}
And the final relevant file is :
template <typename T>
class ThreadT
{
public:
typedef enum
{
NOT_YET_STARTED,
RUNNING,
DEAD,
}thread_status;
private:
thread_status status_;
pthread_t thread_;
public:
/* .... */
void start(void *(*ptr)(void *), void* data){
this->status_ = RUNNING;
if (pthread_create(&this->thread_, NULL, ptr, data) != 0)
throw (SystemError("pthread_create"));
}
/* ...... */
};
You aren't using a templated function instantiation when you pass the pointer to your 'trampoline function' to the ThreadT<T>::start() method, you'll need to specify for which T it should be instantiated:
it->start(run_tramp<T>, &(*it));
// ^^^