Skip to content

UserGuide

Kirill edited this page Jul 19, 2015 · 1 revision

Getting sources

You need checkout source code from the trunk of the project repository, please find links at Source page or download archive from Releases page.

Compilation remarks

On Windows platform library usually can be simply included to the project. But on Posix platforms you have to add "pthread" library to your linker, for gcc it can be done by adding "-pthread" command line parameter.

Building examples

Root directory of the project contain sub directory named "examples" where you can find Premake executable. You can make build scripts for system you like. For example if you need solution for VS2010 you need type in console "premake4 vs2010", after that you will get directory named "build_scripts" where VS projects and solution will be placed.

Thread class

If you need only simple thread implementation, you could use cpptask::Thread class, where you will need override Run method of the class. Or you can use cpptask::ThreadFunction class, which take functor as constructor parameter.

#include <cpptask.h>
...
typedef void (*ThreadFunc)(void);
void ThreadFunc1()
{
   //do work
}
...
spptask::ThreadFunction<ThreadFunc> mythread(&ThreadFunc1);
mythread.Start();
...
mythread.Wait();
...

Atomic variables

Library provide only two types of atomics values:

  • cpptask::AtomicFlag - class represents boolean values you can Set flag and Reset it.
#include <cpptask.h>
...
cpptask::AtomicFlag someFlag;
someFlag.Set();
if (someFlag.IsSet())
{
    ...
    someFlag.Reset();
}
...
  • cpptask::AtomicNumber - class represents c++ long value type you can atomicaly Inc/Dec and SetValue/GetValue from it.
#include <cpptask.h>
...
cpptask::AtomicNumber counter;
counter.SetValue(10);
...
counter.Inc();
...
counter.Dec();
...
if (counter.GetValue() == 10)
{
    ...
}
...

Mutex primitive

Standard Mutex synchronization primitive:

#include <cpptask.h>
...
cpptask::Mutex guard;
...
{
   cpptask::ScopedLock lock(&guard);
   // synchronized work
   ...
}
...
long timeInMiliseconds = 10;
if (quard.WaitLock(timeInMiliseconds))
{
   // wait for given time when mutex will be released and make a lock
   ...
   quard.UnLock();
}
...

Event primitive

Windows API like Event synchronization primitive. One thread can wait for an event which can be signaled from many other threads.

#include <cpptask.h>
...
cpptask::Event event;
...
event.Signal();
...
event.Wait();
// do work work
event.Reset();
...
std::vector<MultWaitBase*> events;
int signaled = WaitForMultiple(events);
switch (signaled)
{
case -1: 
    // error
    break;
case 0:
    // first event is in signaled state
    break;
case 1:
    // second event is in signaled state
    break;
}

Semaphore primitive

Semaphore synchronization primitive. Multiple threads can signal and wait for semaphore. Each signal call will increase semaphore counter. Wait call will return if counter is non zero, and decrement it. If counter is zero Wait call will lock thread.

#include <cpptask.h>
...
cpptask::Semaphore sem;
...
sem.Signal();
...
event.Wait();
// do work work

...
std::vector<MultWaitBase*> events;
events.push_back(&sem);
int signaled = WaitForMultiple(sem);
switch (signaled)
{
case -1: 
    // error
    break;
case 0:
    // first event is in signaled state
    break;
case 1:
    // second event is in signaled state
    break;
}

Task class for faster job delivering to threads

To schedule some job to the thread engine you can derive your class from the Task class and override Execute method. Then you have to create and initialize TaskManager object. After that you have to add task to the manager and ask manager to start new jobs. Also Task class have method Wait which will lock you thread until task will be processed, and have method GetLastException which you can call after wait to check if there were some exceptions.

#include "cpptask.h"
...
class MyTask : public Task
{
public:
    virtual void Execute()
    {
        ...
    }
};
...
#include "cpptask.h"
...
cpptask::TaskThreadPool threadPool(THREADS_NUM);
...
cpptask::TaskManager* manager = cpptask::TaskManager::GetCurrent();
std::shared_ptr<MyTask> task(new MyTask);
manager.AddTask(task.get());
...
task->Wait(); //lock thread
...
manager.WaitTask(task.get()); //execute other tasks during waiting

Parallel-For algorithm

Algorithm to execute in parallel some identical operations on container's elements.

#include <cpptask.h>
...
cpptask::TaskThreadPool threadPool(THREADS_NUM);
...
void func()(double& x)
{
   //some operation
}
...
std::vector<double> array;
...
cpptask::ParallelFor(array.begin(), array.end(), func);

Parallel-Reduce algorithm

It is a some sort of parallel aggregation algorithm. The result of it will be the same as after std::accumulate. To use such algorithm you have to implement class with next three methods:

  1. Copy constructor which will take as second parameter cpptask::SplitMark type - will be called to separate initial range to smaller ranges and divide them between threads.

  2. Function operator which will take Range object to perform some operations on this range

  3. Join method which will be called to combine results from subranges processing.

#include <cpptask.h>
...
typedef std::vector<double> ArrayType;

class Accumulator
{
public:
    Accumulator(double init) : res(init){}
    Accumulator(const Accumulator& accumulator, cpptask::SplitMark)
        : res(accumulator.res){}

    void operator()(const cpptask::Range<ArrayType::iterator>& range)
    {
        ArrayType::iterator i = range.start;
        ArrayType::iterator e = range.end;
        for(; i != e; ++i)
        {
            res += i;
        };
    }

    void Join(const Accumulator& accumulator)
    {
        res += accumulator.res;
    }

    double res;
};
...
Accumulator accumulator(0);
cpptask::ParallelReduce(array.begin(), array.end(), accumulator);
std::cout << accumulator.res;
...

Parallel-Invoke algorithm

Algorithm simply execute two functions in parallel.

#include <cpptask.h>
...
cpptask::ParallelInvoke(func1, func2);

Delivering exceptions from threads and tasks to caller

If you want to get exceptions from threads or tasks you have to derive this exceptions from cpptask::Exception class. After throwing such exception you will be able to get them from GetLastException method of Thread and Task classes. If you will throw another exception they will be hidden, and there will be only one way to know Thread execution result call Thread::GetExitCode method. Also all cpptask algorithms will automatically rethrow cpptask::Exception after they finished.

Lambda expressions support

New C++11 lambdas can be used in all algorithms of the library.

cpptask::ParallelInvoke([&](){//expensive work}, [&](){//expensive work});