First commit
This commit is contained in:
commit
1a56e9bc1d
26
Makefile
Normal file
26
Makefile
Normal file
@ -0,0 +1,26 @@
|
||||
CXX = g++
|
||||
INC_DIR = include
|
||||
OBJ_DIR = object
|
||||
SRC_DIR = src
|
||||
EXE_DIR = bin
|
||||
CFLAGS = -O3 -ffast-math -std=c++98 -pedantic -pthread
|
||||
TEST_FILE = $(EXE_DIR)/SumNums
|
||||
TARGET = $(EXE_DIR)/SumNums
|
||||
MKDIR_P = mkdir -p
|
||||
|
||||
SRC = $(SRC_DIR)/ThreadPool_pthread.cpp $(SRC_DIR)/LockGuard_pthread.cpp examples/sumnums.cpp
|
||||
OBJ = $(OBJ_DIR)/ThreadPool_pthread.o $(OBJ_DIR)/LockGuard_pthread.o $(OBJ_DIR)/sumnums.o
|
||||
DEP = $(INC_DIR)/ThreadPool_pthread.h $(INC_DIR)/LockGuard_pthread.h
|
||||
|
||||
all: $(TARGET)
|
||||
|
||||
$(OBJ): $(SRC) $(DEP)
|
||||
$(MKDIR_P) $(@D)
|
||||
$(CXX) $(CFLAGS) -I$(INC_DIR) -c $< -o $@
|
||||
|
||||
$(TARGET): $(OBJ) $(DEP)
|
||||
$(MKDIR_P) $(@D)
|
||||
$(CXX) $(CFLAGS) -o $@ $(SRC) -I$(INC_DIR)
|
||||
|
||||
clean:
|
||||
$(RM) $(OBJ_DIR)/*.o $(TARGET) $(TEST_FILE)
|
27
Threadpool_pthread.pro
Normal file
27
Threadpool_pthread.pro
Normal file
@ -0,0 +1,27 @@
|
||||
QT -= core
|
||||
QT -= gui
|
||||
|
||||
CONFIG += c++98
|
||||
|
||||
TARGET = Threadpool_pthread
|
||||
CONFIG += console
|
||||
CONFIG -= app_bundle
|
||||
CONFIG -= qt
|
||||
|
||||
TEMPLATE = app
|
||||
|
||||
SOURCES += \
|
||||
src/LockGuard_pthread.cpp \
|
||||
src/ThreadPool_pthread.cpp \
|
||||
examples/sumnums.cpp
|
||||
|
||||
HEADERS += \
|
||||
include/LockGuard_pthread.h \
|
||||
include/ThreadPool_pthread.h
|
||||
|
||||
OTHER_FILES += Makefile
|
||||
|
||||
INCLUDEPATH += include
|
||||
|
||||
LIBS += -pthread
|
||||
|
37
examples/sumnums.cpp
Normal file
37
examples/sumnums.cpp
Normal file
@ -0,0 +1,37 @@
|
||||
#include "../include/ThreadPool_pthread.h"
|
||||
#include <iostream>
|
||||
#include <cstdlib>
|
||||
|
||||
void* SumZeroToNumber(void* num)
|
||||
{
|
||||
long val = 0;
|
||||
for(long i = 0; i <= *static_cast<long*>(num); i++) {
|
||||
val += i;
|
||||
}
|
||||
*static_cast<long*>(num) = val;
|
||||
return NULL;
|
||||
}
|
||||
int main()
|
||||
{
|
||||
std::vector<long> nums(10000);
|
||||
std::vector<long> numsResults(10000);
|
||||
try
|
||||
{
|
||||
ThreadPool_pthread pool(8); //num of cores
|
||||
for(unsigned i = 0; i < nums.size(); i++) {
|
||||
nums[i] = rand() % 1000;
|
||||
numsResults[i] = nums[i]; //this is just a copy to be passed and modified, while the original remains unchanged
|
||||
pool.push_task(SumZeroToNumber,static_cast<void*>(&numsResults[i]));
|
||||
}
|
||||
pool.finish();
|
||||
}
|
||||
catch(std::exception &ex)
|
||||
{
|
||||
std::cout<<"An exception was thrown while processing data. Exception says: " << ex.what() << std::endl;
|
||||
std::exit(1);
|
||||
}
|
||||
for (unsigned int i = 0; i < nums.size(); ++i) {
|
||||
std::cout<<"Sum from 0 to " << nums[i] << " is: " << numsResults[i] <<std::endl;
|
||||
}
|
||||
return 0;
|
||||
}
|
28
include/LockGuard_pthread.h
Normal file
28
include/LockGuard_pthread.h
Normal file
@ -0,0 +1,28 @@
|
||||
#ifndef LOCKGUARD_H
|
||||
#define LOCKGUARD_H
|
||||
|
||||
#include "pthread.h"
|
||||
|
||||
/**
|
||||
* @brief The LockGuard class
|
||||
* Protects against deadlock by forcing unlocks when going out of scope
|
||||
*
|
||||
* The mechanism is quite simple. Constructor locks, destructor unlocks,
|
||||
* and custom locks and unlocks are possible
|
||||
*/
|
||||
class LockGuard_pthread
|
||||
{
|
||||
pthread_mutex_t& _lock;
|
||||
bool is_locked;
|
||||
public:
|
||||
LockGuard_pthread(pthread_mutex_t& LockRef);
|
||||
~LockGuard_pthread();
|
||||
void lock();
|
||||
void unlock();
|
||||
|
||||
private:
|
||||
LockGuard_pthread(LockGuard_pthread const &);
|
||||
void operator=(LockGuard_pthread &);
|
||||
};
|
||||
|
||||
#endif // LOCKGUARD_H
|
89
include/ThreadPool_pthread.h
Normal file
89
include/ThreadPool_pthread.h
Normal file
@ -0,0 +1,89 @@
|
||||
#ifndef THREADPOOL_H
|
||||
#define THREADPOOL_H
|
||||
|
||||
#include <queue>
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
||||
#include "pthread.h"
|
||||
|
||||
#include "LockGuard_pthread.h"
|
||||
|
||||
/**
|
||||
* @brief The ThreadPool class
|
||||
* A basic thread pool implementation using pthread
|
||||
*
|
||||
* Call with the constructor ThreadPool::ThreadPool(N) to get N threads.
|
||||
* The default constructor will detect the number of available cores/core threads and use it.
|
||||
* Constructor throws std::runtime_error() on failure to spawn processes.
|
||||
*
|
||||
*
|
||||
* Add tasks using ThreadPool::push_task(function, args). More info at the function description.
|
||||
*
|
||||
* Use ThreadPool::finish() to wait for all tasks to process and join all threads.
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
class ThreadPool_pthread
|
||||
{
|
||||
pthread_cond_t task_queue_cond; //signaled when a new task is pushed
|
||||
pthread_cond_t thread_finished_cond; //signaled when a thread finishes and no more is there to do
|
||||
pthread_mutex_t mutex;
|
||||
bool no_more_to_push;
|
||||
|
||||
typedef void*(*FunctionType)(void*);
|
||||
|
||||
//queue of tasks as an std::queue of std::pair of functions and args
|
||||
std::queue<std::pair<FunctionType,void*> > tasks_queue;
|
||||
|
||||
|
||||
void launch_threads();
|
||||
int num_of_threads; //stores the number of threads requested
|
||||
std::vector<pthread_t> threads_objects;
|
||||
friend void* _thread_worker(void*);
|
||||
bool joined; //prevents rejoining twice, true when all threads are joined
|
||||
long threads_running; //keeps track of threads that are executing tasks
|
||||
void join_all();
|
||||
|
||||
public:
|
||||
ThreadPool_pthread(int threads_count);
|
||||
virtual ~ThreadPool_pthread();
|
||||
|
||||
/**
|
||||
* @brief push_task
|
||||
* Adds a task to the processing queue
|
||||
*
|
||||
* Results are passed through parameters too. No return value is considered.
|
||||
*
|
||||
* @param function
|
||||
* Function pointer to the function to be executed.
|
||||
* The function is of type void*(void*)
|
||||
* All parameters should be passed through a single void pointer
|
||||
*
|
||||
* @param params
|
||||
* Parameters of the function as void*.
|
||||
*/
|
||||
void push_task(FunctionType function, void* params);
|
||||
|
||||
/**
|
||||
* @brief finish
|
||||
* Waits for all threads to finish and joins them.
|
||||
*/
|
||||
void finish();
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
std::string _to_string(T const& value)
|
||||
{
|
||||
std::stringstream sstr;
|
||||
|
||||
sstr << value;
|
||||
return sstr.str();
|
||||
}
|
||||
|
||||
#endif // THREADPOOL_H
|
29
src/LockGuard_pthread.cpp
Normal file
29
src/LockGuard_pthread.cpp
Normal file
@ -0,0 +1,29 @@
|
||||
#include "LockGuard_pthread.h"
|
||||
|
||||
LockGuard_pthread::LockGuard_pthread(pthread_mutex_t &LockRef) : _lock(LockRef), is_locked(false)
|
||||
{
|
||||
lock();
|
||||
}
|
||||
|
||||
LockGuard_pthread::~LockGuard_pthread()
|
||||
{
|
||||
unlock();
|
||||
}
|
||||
|
||||
void LockGuard_pthread::lock()
|
||||
{
|
||||
if(!is_locked)
|
||||
{
|
||||
pthread_mutex_lock(&_lock);
|
||||
is_locked = true;
|
||||
}
|
||||
}
|
||||
|
||||
void LockGuard_pthread::unlock()
|
||||
{
|
||||
if(is_locked)
|
||||
{
|
||||
pthread_mutex_unlock(&_lock);
|
||||
is_locked = false;
|
||||
}
|
||||
}
|
117
src/ThreadPool_pthread.cpp
Normal file
117
src/ThreadPool_pthread.cpp
Normal file
@ -0,0 +1,117 @@
|
||||
#include "ThreadPool_pthread.h"
|
||||
#include <string>
|
||||
#include <stdexcept>
|
||||
|
||||
void* _thread_worker(void* pool_obj)
|
||||
{
|
||||
ThreadPool_pthread* pool = static_cast<ThreadPool_pthread*>(pool_obj);
|
||||
while(true)
|
||||
{
|
||||
//variable to store a pointer to the function and the parameters
|
||||
std::pair<ThreadPool_pthread::FunctionType,void*> func_and_args;
|
||||
LockGuard_pthread guard(pool->mutex);
|
||||
|
||||
//if the queue isn't empty, and finish() isn't called, wait for more tasks
|
||||
while (pool->tasks_queue.empty() && !pool->no_more_to_push)
|
||||
{
|
||||
pthread_cond_wait(&pool->task_queue_cond, &pool->mutex);
|
||||
}
|
||||
//pull task from queue and execute it if the queue isn't empty
|
||||
if(!pool->tasks_queue.empty())
|
||||
{
|
||||
func_and_args = pool->tasks_queue.front();
|
||||
pool->tasks_queue.pop();
|
||||
|
||||
guard.unlock();
|
||||
|
||||
//execute task
|
||||
func_and_args.first(func_and_args.second);
|
||||
guard.lock();
|
||||
}
|
||||
//if the queue is empty and a "finished" signal was given, exit the
|
||||
//loop and signal that this thread is finished
|
||||
if(pool->tasks_queue.empty() && pool->no_more_to_push)
|
||||
{
|
||||
pool->threads_running--;
|
||||
pthread_cond_signal(&pool->thread_finished_cond);
|
||||
break;
|
||||
}
|
||||
}
|
||||
pthread_exit(NULL);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void ThreadPool_pthread::launch_threads()
|
||||
{
|
||||
threads_objects.resize(num_of_threads);
|
||||
int rc;
|
||||
std::cout << "Launching " << num_of_threads << " threads." << std::endl;
|
||||
|
||||
//prevent starting tasks while launching threads
|
||||
LockGuard_pthread guard(mutex);
|
||||
for (int i=0; i < num_of_threads; ++i)
|
||||
{
|
||||
rc = pthread_create(&threads_objects[i], 0, _thread_worker, static_cast<void*>(this));
|
||||
if(rc)
|
||||
{
|
||||
this->finish();
|
||||
throw std::runtime_error("Unable to launch threads. Error code " + _to_string(rc));
|
||||
}
|
||||
threads_running++;
|
||||
}
|
||||
std::cout << "Launching " << num_of_threads << " threads successful." << std::endl;
|
||||
}
|
||||
|
||||
void ThreadPool_pthread::join_all()
|
||||
{
|
||||
if(!joined)
|
||||
{
|
||||
for(unsigned i = 0; i < threads_objects.size(); i++)
|
||||
{
|
||||
pthread_join(threads_objects[i],NULL);
|
||||
}
|
||||
joined = true;
|
||||
}
|
||||
}
|
||||
|
||||
void ThreadPool_pthread::finish()
|
||||
{
|
||||
LockGuard_pthread guard(this->mutex);
|
||||
this->no_more_to_push = true;
|
||||
pthread_cond_broadcast(&this->task_queue_cond);
|
||||
while(threads_running > 0)
|
||||
{
|
||||
pthread_cond_wait(&this->thread_finished_cond, &this->mutex);
|
||||
}
|
||||
guard.unlock();
|
||||
join_all();
|
||||
}
|
||||
|
||||
ThreadPool_pthread::~ThreadPool_pthread()
|
||||
{
|
||||
this->finish();
|
||||
pthread_mutex_destroy(&mutex);
|
||||
pthread_cond_destroy(&task_queue_cond);
|
||||
pthread_cond_destroy(&thread_finished_cond);
|
||||
}
|
||||
|
||||
ThreadPool_pthread::ThreadPool_pthread(int threads_count)
|
||||
{
|
||||
pthread_mutex_init(&mutex, NULL);
|
||||
no_more_to_push = false;
|
||||
threads_running = 0;
|
||||
joined = true;
|
||||
pthread_cond_init(&task_queue_cond, 0);
|
||||
pthread_cond_init(&thread_finished_cond, 0);
|
||||
num_of_threads = threads_count;
|
||||
joined = false;
|
||||
no_more_to_push = false;
|
||||
launch_threads();
|
||||
}
|
||||
|
||||
void ThreadPool_pthread::push_task(ThreadPool_pthread::FunctionType func, void *params)
|
||||
{
|
||||
LockGuard_pthread guard(mutex);
|
||||
this->tasks_queue.push(std::make_pair(func,params));
|
||||
pthread_cond_signal(&task_queue_cond);
|
||||
}
|
Loading…
Reference in New Issue
Block a user