// 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 #include // for dlib::sleep #include #include 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 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 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 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["<