mirror of https://github.com/davisking/dlib.git
Improved the thread_pool example program.
This commit is contained in:
parent
cf8e3d6f1e
commit
b6ae2a6d68
|
@ -13,6 +13,7 @@
|
|||
#include "dlib/threads.h"
|
||||
#include "dlib/misc_api.h" // for dlib::sleep
|
||||
#include "dlib/logger.h"
|
||||
#include <vector>
|
||||
|
||||
using namespace dlib;
|
||||
|
||||
|
@ -33,65 +34,81 @@ 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 member function on a particular object
|
||||
(or if you use futures you may make tasks that call global functions).
|
||||
So here we create a class called test with a few member functions which
|
||||
we will have the thread pool call as tasks.
|
||||
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_0()
|
||||
void task()
|
||||
{
|
||||
dlog << LINFO << "task_0 start";
|
||||
dlog << LINFO << "task start";
|
||||
|
||||
// Here we ask the thread pool to call this->subtask() three different times
|
||||
// with different arguments. 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_0() is executed within the thread pool (see main() below)
|
||||
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 safe to have
|
||||
// tasks running in the thread pool spawn sub tasks which is what we are doing here.
|
||||
tp.add_task(*this,&test::subtask,1); // schedule call to this->subtask(1)
|
||||
tp.add_task(*this,&test::subtask,2); // schedule call to this->subtask(2)
|
||||
tp.add_task(*this,&test::subtask,3); // schedule call to this->subtask(3)
|
||||
// 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()
|
||||
|
||||
// wait_for_all_tasks() is a function that blocks until all tasks
|
||||
// submitted to the thread pool by the thread calling wait_for_all_tasks()
|
||||
// finish. So this call blocks until the 3 tasks above are done.
|
||||
// Since var is a future, this line will wait for the test::subtask task to
|
||||
// finish and before allowing us to access the contents of var. var will
|
||||
// return the integer it contains. In this case result will be assigned
|
||||
// the value of 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_0 end" ;
|
||||
dlog << LINFO << "task end" ;
|
||||
}
|
||||
|
||||
void subtask(long a)
|
||||
void subtask(int& a)
|
||||
{
|
||||
dlib::sleep(200);
|
||||
dlog << LINFO << "subtask end " << a;
|
||||
a = a + 1;
|
||||
dlog << LINFO << "subtask end ";
|
||||
}
|
||||
|
||||
void task_1(long a, long b)
|
||||
void subtask2()
|
||||
{
|
||||
dlog << LINFO << "task_1 start: " << a << ", " << b;
|
||||
dlib::sleep(700);
|
||||
dlog << LINFO << "task_1 end: " << a << ", " << b;
|
||||
dlib::sleep(300);
|
||||
dlog << LINFO << "subtask2 end ";
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------------------
|
||||
|
||||
void add (
|
||||
long a,
|
||||
long b,
|
||||
long& result
|
||||
)
|
||||
class add_value
|
||||
{
|
||||
dlib::sleep(400);
|
||||
result = a + b;
|
||||
}
|
||||
public:
|
||||
add_value(int value):val(value) { }
|
||||
|
||||
void operator()( int& a )
|
||||
{
|
||||
a += val;
|
||||
}
|
||||
|
||||
private:
|
||||
int val;
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------------------
|
||||
|
||||
|
@ -100,56 +117,58 @@ int main()
|
|||
// tell the logger to print out everything
|
||||
dlog.set_level(LALL);
|
||||
|
||||
test a;
|
||||
|
||||
dlog << LINFO << "schedule a few tasks";
|
||||
|
||||
// schedule a call to a.task_1(10,11)
|
||||
tp.add_task(a, &test::task_1, 10, 11);
|
||||
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);
|
||||
|
||||
// schedule the thread pool to call a.task_0().
|
||||
uint64 id = tp.add_task(a, &test::task_0);
|
||||
// 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
|
||||
|
||||
// schedule a call to a.task_1(12,13)
|
||||
tp.add_task(a, &test::task_1, 12, 13);
|
||||
|
||||
dlog << LINFO << "wait for a.task_0() to finish";
|
||||
// now wait for our a.task_0() task to finish. To do this we use the id
|
||||
// returned by add_task to reference the task we want to wait for.
|
||||
tp.wait_for_task(id);
|
||||
dlog << LINFO << "a.task_0() finished, now start another task_1() call";
|
||||
|
||||
// schedule a call to a.task_1(14,15)
|
||||
tp.add_task(a, &test::task_1, 14, 15);
|
||||
|
||||
dlog << LINFO << "wait for all tasks to finish";
|
||||
// here we wait for all tasks which were requested by the main thread
|
||||
// to complete.
|
||||
|
||||
// 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();
|
||||
dlog << LINFO << "all tasks finished";
|
||||
|
||||
|
||||
|
||||
// The thread pool also allows you to use futures to pass arbitrary objects into the tasks.
|
||||
// For example:
|
||||
future<long> n1, n2, result;
|
||||
n1 = 3;
|
||||
n2 = 4;
|
||||
// add a task that is supposed to go call add(n1, n2, result);
|
||||
tp.add_task(add, n1, n2, result);
|
||||
|
||||
// This line will wait for the task in the thread pool to finish and when it does
|
||||
// result will return the integer it contains. In this case r will be assigned a value of 7.
|
||||
long r = result;
|
||||
// print out the result
|
||||
dlog << LINFO << "result = " << r;
|
||||
|
||||
// We can also use futures with member functions like so:
|
||||
tp.add_task(a, &test::task_1, n1, n2);
|
||||
|
||||
// and we can still wait for tasks like so:
|
||||
tp.wait_for_all_tasks();
|
||||
dlog << LINFO << "all tasks using futures finished";
|
||||
for (unsigned long i = 0; i < vect.size(); ++i)
|
||||
{
|
||||
dlog << LINFO << "vect["<<i<<"]: " << vect[i];
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
@ -157,26 +176,24 @@ int main()
|
|||
the time the log message occurred and the value in [] is the thread id for the thread
|
||||
that generated the log message):
|
||||
|
||||
0 INFO [0] main: schedule a few tasks
|
||||
0 INFO [1] main: task_1 start: 10, 11
|
||||
0 INFO [2] main: task_0 start
|
||||
200 INFO [2] main: subtask end 2
|
||||
200 INFO [3] main: subtask end 1
|
||||
200 INFO [3] main: task_1 start: 12, 13
|
||||
201 INFO [0] main: wait for a.task_0() to finish
|
||||
400 INFO [2] main: subtask end 3
|
||||
400 INFO [2] main: task_0 end
|
||||
400 INFO [0] main: a.task_0() finished, now start another task_1() call
|
||||
401 INFO [2] main: task_1 start: 14, 15
|
||||
401 INFO [0] main: wait for all tasks to finish
|
||||
700 INFO [1] main: task_1 end: 10, 11
|
||||
901 INFO [3] main: task_1 end: 12, 13
|
||||
1101 INFO [2] main: task_1 end: 14, 15
|
||||
1101 INFO [0] main: all tasks finished
|
||||
1503 INFO [0] main: result = 7
|
||||
1503 INFO [3] main: task_1 start: 3, 4
|
||||
2203 INFO [3] main: task_1 end: 3, 4
|
||||
2203 INFO [0] main: all tasks using futures finished
|
||||
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
|
||||
*/
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue