mirror of https://github.com/davisking/dlib.git
204 lines
6.8 KiB
C++
204 lines
6.8 KiB
C++
// The contents of this file are in the public domain. See LICENSE_FOR_EXAMPLE_PROGRAMS.txt
|
|
/*
|
|
|
|
This is an example illustrating the use of the thread_pool
|
|
object from the dlib C++ Library.
|
|
|
|
|
|
This is a very simple example. It creates a thread pool with 3
|
|
threads and then sends a few simple tasks to the pool.
|
|
*/
|
|
|
|
|
|
#include <dlib/threads.h>
|
|
#include <dlib/misc_api.h> // for dlib::sleep
|
|
#include <dlib/logger.h>
|
|
#include <vector>
|
|
|
|
using namespace dlib;
|
|
|
|
// We will be using the dlib logger object to print out messages in this example
|
|
// because its output is timestamped and labeled with the thread that the log
|
|
// message came from. So this will make it easier to see what is going on in
|
|
// this example. Here we make an instance of the logger. See the logger
|
|
// documentation and examples for detailed information regarding its use.
|
|
logger dlog("main");
|
|
|
|
|
|
// Here we make an instance of the thread pool object
|
|
thread_pool tp(3);
|
|
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
class test
|
|
{
|
|
/*
|
|
The thread_pool accepts "tasks" from the user and schedules them for
|
|
execution in one of its threads when one becomes available. Each task
|
|
is just a request to call a function. So here we create a class called
|
|
test with a few member functions which we will have the thread pool call
|
|
as tasks.
|
|
*/
|
|
public:
|
|
|
|
void task()
|
|
{
|
|
dlog << LINFO << "task start";
|
|
|
|
future<int> var;
|
|
|
|
var = 1;
|
|
|
|
// Here we ask the thread pool to call this->subtask() and this->subtask2().
|
|
// Note that calls to add_task() will return immediately if there is an
|
|
// available thread to hand the task off to. However, if there isn't a
|
|
// thread ready then add_task() blocks until there is such a thread.
|
|
// Also note that since task() is executed within the thread pool (see main() below)
|
|
// calls to add_task() will execute the requested task within the calling thread
|
|
// in cases where the thread pool is full. This means it is always safe to
|
|
// spawn subtasks from within another task, which is what we are doing here.
|
|
tp.add_task(*this,&test::subtask,var); // schedule call to this->subtask(var)
|
|
tp.add_task(*this,&test::subtask2); // schedule call to this->subtask2()
|
|
|
|
// Since var is a future, this line will wait for the test::subtask task to
|
|
// finish before allowing us to access the contents of var. Then var will
|
|
// return the integer it contains. In this case result will be assigned
|
|
// the value 2 since var was incremented by subtask().
|
|
int result = var;
|
|
// print out the result
|
|
dlog << LINFO << "var = " << result;
|
|
|
|
// Wait for all the tasks we have started to finish. Note that
|
|
// wait_for_all_tasks() only waits for tasks which were started
|
|
// by the calling thread. So you don't have to worry about other
|
|
// unrelated parts of your application interfering. In this case
|
|
// it just waits for subtask2() to finish.
|
|
tp.wait_for_all_tasks();
|
|
|
|
dlog << LINFO << "task end" ;
|
|
}
|
|
|
|
void subtask(int& a)
|
|
{
|
|
dlib::sleep(200);
|
|
a = a + 1;
|
|
dlog << LINFO << "subtask end ";
|
|
}
|
|
|
|
void subtask2()
|
|
{
|
|
dlib::sleep(300);
|
|
dlog << LINFO << "subtask2 end ";
|
|
}
|
|
|
|
};
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
class add_value
|
|
{
|
|
public:
|
|
add_value(int value):val(value) { }
|
|
|
|
void operator()( int& a )
|
|
{
|
|
a += val;
|
|
}
|
|
|
|
private:
|
|
int val;
|
|
};
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
int main()
|
|
{
|
|
// tell the logger to print out everything
|
|
dlog.set_level(LALL);
|
|
|
|
|
|
dlog << LINFO << "schedule a few tasks";
|
|
|
|
test mytask;
|
|
// Schedule the thread pool to call mytask.task(). Note that all forms of add_task()
|
|
// pass in the task object by reference. This means you must make sure, in this case,
|
|
// that mytask isn't destructed until after the task has finished executing.
|
|
tp.add_task(mytask, &test::task);
|
|
|
|
// You can also pass task objects to a thread pool by value. So in this case we don't
|
|
// have to worry about keeping our own instance of the task. Here we construct a temporary
|
|
// add_value object and pass it right in and everything works like it should.
|
|
future<int> num = 3;
|
|
tp.add_task_by_value(add_value(7), num); // adds 7 to num
|
|
int result = num.get();
|
|
dlog << LINFO << "result = " << result; // prints result = 10
|
|
|
|
|
|
|
|
|
|
|
|
// uncomment this line if your compiler supports the new C++0x lambda functions
|
|
//#define COMPILER_SUPPORTS_CPP0X_LAMBDA_FUNCTIONS
|
|
#ifdef COMPILER_SUPPORTS_CPP0X_LAMBDA_FUNCTIONS
|
|
|
|
// In the above examples we had to explicitly create task objects which is
|
|
// inconvenient. If you have a compiler which supports C++0x lambda functions
|
|
// then you can use the following simpler method.
|
|
|
|
// make a task which will just log a message
|
|
tp.add_task_by_value([](){
|
|
dlog << LINFO << "A message from a lambda function running in another thread.";
|
|
});
|
|
|
|
// Here we make 10 different tasks, each assigns a different value into
|
|
// the elements of the vector vect.
|
|
std::vector<int> vect(10);
|
|
for (unsigned long i = 0; i < vect.size(); ++i)
|
|
{
|
|
// Make a lambda function which takes vect by reference and i by value. So what
|
|
// will happen is each assignment statement will run in a thread in the thread_pool.
|
|
tp.add_task_by_value([&vect,i](){
|
|
vect[i] = i;
|
|
});
|
|
}
|
|
// Wait for all tasks which were requested by the main thread to complete.
|
|
tp.wait_for_all_tasks();
|
|
for (unsigned long i = 0; i < vect.size(); ++i)
|
|
{
|
|
dlog << LINFO << "vect["<<i<<"]: " << vect[i];
|
|
}
|
|
#endif
|
|
|
|
|
|
|
|
/* A possible run of this program might produce the following output (the first column is
|
|
the time the log message occurred and the value in [] is the thread id for the thread
|
|
that generated the log message):
|
|
|
|
1 INFO [0] main: schedule a few tasks
|
|
1 INFO [1] main: task start
|
|
1 INFO [0] main: result = 10
|
|
201 INFO [2] main: subtask end
|
|
201 INFO [1] main: var = 2
|
|
201 INFO [2] main: A message from a lambda function running in another thread.
|
|
301 INFO [3] main: subtask2 end
|
|
301 INFO [1] main: task end
|
|
301 INFO [0] main: vect[0]: 0
|
|
301 INFO [0] main: vect[1]: 1
|
|
301 INFO [0] main: vect[2]: 2
|
|
301 INFO [0] main: vect[3]: 3
|
|
301 INFO [0] main: vect[4]: 4
|
|
301 INFO [0] main: vect[5]: 5
|
|
301 INFO [0] main: vect[6]: 6
|
|
301 INFO [0] main: vect[7]: 7
|
|
301 INFO [0] main: vect[8]: 8
|
|
301 INFO [0] main: vect[9]: 9
|
|
*/
|
|
}
|
|
|
|
|
|
|
|
|
|
|