mirror of
https://github.com/davisking/dlib.git
synced 2024-11-01 10:14:53 +08:00
Made thread_pool and parallel_for propagate exceptions from task threads to
calling code.
This commit is contained in:
parent
5b36194513
commit
4ee1f6644d
@ -369,6 +369,39 @@ namespace
|
|||||||
DLIB_TEST(d == 4);
|
DLIB_TEST(d == 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
tp.wait_for_all_tasks();
|
||||||
|
|
||||||
|
// make sure exception propagation from tasks works correctly.
|
||||||
|
auto f_throws = []() { throw dlib::error("test exception");};
|
||||||
|
bool got_exception = false;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
tp.add_task_by_value(f_throws);
|
||||||
|
tp.wait_for_all_tasks();
|
||||||
|
}
|
||||||
|
catch(dlib::error& e)
|
||||||
|
{
|
||||||
|
DLIB_TEST(e.info == "test exception");
|
||||||
|
got_exception = true;
|
||||||
|
}
|
||||||
|
DLIB_TEST(got_exception);
|
||||||
|
|
||||||
|
dlib::future<int> aa;
|
||||||
|
auto f_throws2 = [](int& a) { a = 1; throw dlib::error("test exception");};
|
||||||
|
got_exception = false;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
tp.add_task(f_throws2, aa);
|
||||||
|
aa.get();
|
||||||
|
}
|
||||||
|
catch(dlib::error& e)
|
||||||
|
{
|
||||||
|
DLIB_TEST(e.info == "test exception");
|
||||||
|
got_exception = true;
|
||||||
|
}
|
||||||
|
DLIB_TEST(got_exception);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,7 +24,6 @@ namespace dlib
|
|||||||
requires
|
requires
|
||||||
- begin <= end
|
- begin <= end
|
||||||
- chunks_per_thread > 0
|
- chunks_per_thread > 0
|
||||||
- funct does not throw any exceptions
|
|
||||||
ensures
|
ensures
|
||||||
- This is a convenience function for submitting a block of jobs to a thread_pool.
|
- This is a convenience function for submitting a block of jobs to a thread_pool.
|
||||||
In particular, given the half open range [begin, end), this function will
|
In particular, given the half open range [begin, end), this function will
|
||||||
@ -61,7 +60,6 @@ namespace dlib
|
|||||||
requires
|
requires
|
||||||
- begin <= end
|
- begin <= end
|
||||||
- chunks_per_thread > 0
|
- chunks_per_thread > 0
|
||||||
- funct does not throw any exceptions
|
|
||||||
ensures
|
ensures
|
||||||
- This function is equivalent to the following block of code:
|
- This function is equivalent to the following block of code:
|
||||||
thread_pool tp(num_threads);
|
thread_pool tp(num_threads);
|
||||||
@ -82,7 +80,6 @@ namespace dlib
|
|||||||
requires
|
requires
|
||||||
- chunks_per_thread > 0
|
- chunks_per_thread > 0
|
||||||
- begin <= end
|
- begin <= end
|
||||||
- funct does not throw any exceptions
|
|
||||||
ensures
|
ensures
|
||||||
- This is a convenience function for submitting a block of jobs to a
|
- This is a convenience function for submitting a block of jobs to a
|
||||||
thread_pool. In particular, given the range [begin, end), this function will
|
thread_pool. In particular, given the range [begin, end), this function will
|
||||||
@ -117,7 +114,6 @@ namespace dlib
|
|||||||
requires
|
requires
|
||||||
- begin <= end
|
- begin <= end
|
||||||
- chunks_per_thread > 0
|
- chunks_per_thread > 0
|
||||||
- funct does not throw any exceptions
|
|
||||||
ensures
|
ensures
|
||||||
- This function is equivalent to the following block of code:
|
- This function is equivalent to the following block of code:
|
||||||
thread_pool tp(num_threads);
|
thread_pool tp(num_threads);
|
||||||
@ -137,7 +133,6 @@ namespace dlib
|
|||||||
requires
|
requires
|
||||||
- begin <= end
|
- begin <= end
|
||||||
- chunks_per_thread > 0
|
- chunks_per_thread > 0
|
||||||
- funct does not throw any exceptions
|
|
||||||
ensures
|
ensures
|
||||||
- This function is equivalent to the following block of code:
|
- This function is equivalent to the following block of code:
|
||||||
parallel_for_blocked(default_thread_pool(), begin, end, funct, chunks_per_thread);
|
parallel_for_blocked(default_thread_pool(), begin, end, funct, chunks_per_thread);
|
||||||
@ -159,7 +154,6 @@ namespace dlib
|
|||||||
requires
|
requires
|
||||||
- begin <= end
|
- begin <= end
|
||||||
- chunks_per_thread > 0
|
- chunks_per_thread > 0
|
||||||
- funct does not throw any exceptions
|
|
||||||
ensures
|
ensures
|
||||||
- This function is equivalent to the following function call:
|
- This function is equivalent to the following function call:
|
||||||
parallel_for_blocked(tp, begin, end, [&](long begin_sub, long end_sub)
|
parallel_for_blocked(tp, begin, end, [&](long begin_sub, long end_sub)
|
||||||
@ -189,7 +183,6 @@ namespace dlib
|
|||||||
requires
|
requires
|
||||||
- begin <= end
|
- begin <= end
|
||||||
- chunks_per_thread > 0
|
- chunks_per_thread > 0
|
||||||
- funct does not throw any exceptions
|
|
||||||
ensures
|
ensures
|
||||||
- This function is equivalent to the following block of code:
|
- This function is equivalent to the following block of code:
|
||||||
thread_pool tp(num_threads);
|
thread_pool tp(num_threads);
|
||||||
@ -210,7 +203,6 @@ namespace dlib
|
|||||||
requires
|
requires
|
||||||
- begin <= end
|
- begin <= end
|
||||||
- chunks_per_thread > 0
|
- chunks_per_thread > 0
|
||||||
- funct does not throw any exceptions
|
|
||||||
ensures
|
ensures
|
||||||
- This function is equivalent to the following function call:
|
- This function is equivalent to the following function call:
|
||||||
parallel_for_blocked(tp, begin, end, [&](long begin_sub, long end_sub)
|
parallel_for_blocked(tp, begin, end, [&](long begin_sub, long end_sub)
|
||||||
@ -238,7 +230,6 @@ namespace dlib
|
|||||||
requires
|
requires
|
||||||
- begin <= end
|
- begin <= end
|
||||||
- chunks_per_thread > 0
|
- chunks_per_thread > 0
|
||||||
- funct does not throw any exceptions
|
|
||||||
ensures
|
ensures
|
||||||
- This function is equivalent to the following block of code:
|
- This function is equivalent to the following block of code:
|
||||||
thread_pool tp(num_threads);
|
thread_pool tp(num_threads);
|
||||||
@ -258,7 +249,6 @@ namespace dlib
|
|||||||
requires
|
requires
|
||||||
- begin <= end
|
- begin <= end
|
||||||
- chunks_per_thread > 0
|
- chunks_per_thread > 0
|
||||||
- funct does not throw any exceptions
|
|
||||||
ensures
|
ensures
|
||||||
- This function is equivalent to the following block of code:
|
- This function is equivalent to the following block of code:
|
||||||
parallel_for(default_thread_pool(), begin, end, funct, chunks_per_thread);
|
parallel_for(default_thread_pool(), begin, end, funct, chunks_per_thread);
|
||||||
@ -280,7 +270,6 @@ namespace dlib
|
|||||||
requires
|
requires
|
||||||
- begin <= end
|
- begin <= end
|
||||||
- chunks_per_thread > 0
|
- chunks_per_thread > 0
|
||||||
- funct does not throw any exceptions
|
|
||||||
ensures
|
ensures
|
||||||
- This function is identical to the parallel_for() routine defined above except
|
- This function is identical to the parallel_for() routine defined above except
|
||||||
that it will print messages to cout showing the progress in executing the
|
that it will print messages to cout showing the progress in executing the
|
||||||
@ -302,7 +291,6 @@ namespace dlib
|
|||||||
requires
|
requires
|
||||||
- begin <= end
|
- begin <= end
|
||||||
- chunks_per_thread > 0
|
- chunks_per_thread > 0
|
||||||
- funct does not throw any exceptions
|
|
||||||
ensures
|
ensures
|
||||||
- This function is identical to the parallel_for() routine defined above except
|
- This function is identical to the parallel_for() routine defined above except
|
||||||
that it will print messages to cout showing the progress in executing the
|
that it will print messages to cout showing the progress in executing the
|
||||||
@ -323,7 +311,6 @@ namespace dlib
|
|||||||
requires
|
requires
|
||||||
- begin <= end
|
- begin <= end
|
||||||
- chunks_per_thread > 0
|
- chunks_per_thread > 0
|
||||||
- funct does not throw any exceptions
|
|
||||||
ensures
|
ensures
|
||||||
- This function is identical to the parallel_for() routine defined above except
|
- This function is identical to the parallel_for() routine defined above except
|
||||||
that it will print messages to cout showing the progress in executing the
|
that it will print messages to cout showing the progress in executing the
|
||||||
@ -344,7 +331,6 @@ namespace dlib
|
|||||||
requires
|
requires
|
||||||
- begin <= end
|
- begin <= end
|
||||||
- chunks_per_thread > 0
|
- chunks_per_thread > 0
|
||||||
- funct does not throw any exceptions
|
|
||||||
ensures
|
ensures
|
||||||
- This function is identical to the parallel_for() routine defined above except
|
- This function is identical to the parallel_for() routine defined above except
|
||||||
that it will print messages to cout showing the progress in executing the
|
that it will print messages to cout showing the progress in executing the
|
||||||
@ -364,7 +350,6 @@ namespace dlib
|
|||||||
requires
|
requires
|
||||||
- begin <= end
|
- begin <= end
|
||||||
- chunks_per_thread > 0
|
- chunks_per_thread > 0
|
||||||
- funct does not throw any exceptions
|
|
||||||
ensures
|
ensures
|
||||||
- This function is identical to the parallel_for() routine defined above except
|
- This function is identical to the parallel_for() routine defined above except
|
||||||
that it will print messages to cout showing the progress in executing the
|
that it will print messages to cout showing the progress in executing the
|
||||||
@ -388,7 +373,6 @@ namespace dlib
|
|||||||
requires
|
requires
|
||||||
- begin <= end
|
- begin <= end
|
||||||
- chunks_per_thread > 0
|
- chunks_per_thread > 0
|
||||||
- funct does not throw any exceptions
|
|
||||||
ensures
|
ensures
|
||||||
- This function is identical to the parallel_for_blocked() routine defined
|
- This function is identical to the parallel_for_blocked() routine defined
|
||||||
above except that it will print messages to cout showing the progress in
|
above except that it will print messages to cout showing the progress in
|
||||||
@ -410,7 +394,6 @@ namespace dlib
|
|||||||
requires
|
requires
|
||||||
- begin <= end
|
- begin <= end
|
||||||
- chunks_per_thread > 0
|
- chunks_per_thread > 0
|
||||||
- funct does not throw any exceptions
|
|
||||||
ensures
|
ensures
|
||||||
- This function is identical to the parallel_for_blocked() routine defined
|
- This function is identical to the parallel_for_blocked() routine defined
|
||||||
above except that it will print messages to cout showing the progress in
|
above except that it will print messages to cout showing the progress in
|
||||||
@ -431,7 +414,6 @@ namespace dlib
|
|||||||
requires
|
requires
|
||||||
- begin <= end
|
- begin <= end
|
||||||
- chunks_per_thread > 0
|
- chunks_per_thread > 0
|
||||||
- funct does not throw any exceptions
|
|
||||||
ensures
|
ensures
|
||||||
- This function is identical to the parallel_for_blocked() routine defined
|
- This function is identical to the parallel_for_blocked() routine defined
|
||||||
above except that it will print messages to cout showing the progress in
|
above except that it will print messages to cout showing the progress in
|
||||||
@ -452,7 +434,6 @@ namespace dlib
|
|||||||
requires
|
requires
|
||||||
- begin <= end
|
- begin <= end
|
||||||
- chunks_per_thread > 0
|
- chunks_per_thread > 0
|
||||||
- funct does not throw any exceptions
|
|
||||||
ensures
|
ensures
|
||||||
- This function is identical to the parallel_for_blocked() routine defined
|
- This function is identical to the parallel_for_blocked() routine defined
|
||||||
above except that it will print messages to cout showing the progress in
|
above except that it will print messages to cout showing the progress in
|
||||||
@ -472,7 +453,6 @@ namespace dlib
|
|||||||
requires
|
requires
|
||||||
- begin <= end
|
- begin <= end
|
||||||
- chunks_per_thread > 0
|
- chunks_per_thread > 0
|
||||||
- funct does not throw any exceptions
|
|
||||||
ensures
|
ensures
|
||||||
- This function is identical to the parallel_for_blocked() routine defined
|
- This function is identical to the parallel_for_blocked() routine defined
|
||||||
above except that it will print messages to cout showing the progress in
|
above except that it will print messages to cout showing the progress in
|
||||||
|
@ -61,6 +61,11 @@ namespace dlib
|
|||||||
}
|
}
|
||||||
|
|
||||||
wait();
|
wait();
|
||||||
|
|
||||||
|
// Throw any unhandled exceptions. Since shutdown_pool() is only called in the
|
||||||
|
// destructor this will kill the program.
|
||||||
|
for (auto&& task : tasks)
|
||||||
|
task.propagate_exception();
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------------------
|
||||||
@ -94,6 +99,9 @@ namespace dlib
|
|||||||
const unsigned long idx = task_id_to_index(task_id);
|
const unsigned long idx = task_id_to_index(task_id);
|
||||||
while (tasks[idx].task_id == task_id)
|
while (tasks[idx].task_id == task_id)
|
||||||
task_done_signaler.wait();
|
task_done_signaler.wait();
|
||||||
|
|
||||||
|
for (auto&& task : tasks)
|
||||||
|
task.propagate_exception();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -124,6 +132,10 @@ namespace dlib
|
|||||||
if (found_task)
|
if (found_task)
|
||||||
task_done_signaler.wait();
|
task_done_signaler.wait();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// throw any exceptions generated by the tasks
|
||||||
|
for (auto&& task : tasks)
|
||||||
|
task.propagate_exception();
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------------------
|
||||||
@ -177,6 +189,9 @@ namespace dlib
|
|||||||
task = tasks[idx];
|
task = tasks[idx];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::exception_ptr eptr;
|
||||||
|
try
|
||||||
|
{
|
||||||
// now do the task
|
// now do the task
|
||||||
if (task.bfp)
|
if (task.bfp)
|
||||||
task.bfp();
|
task.bfp();
|
||||||
@ -186,6 +201,11 @@ namespace dlib
|
|||||||
task.mfp1(task.arg1);
|
task.mfp1(task.arg1);
|
||||||
else if (task.mfp2)
|
else if (task.mfp2)
|
||||||
task.mfp2(task.arg1, task.arg2);
|
task.mfp2(task.arg1, task.arg2);
|
||||||
|
}
|
||||||
|
catch(...)
|
||||||
|
{
|
||||||
|
eptr = std::current_exception();
|
||||||
|
}
|
||||||
|
|
||||||
// Now let others know that we finished the task. We do this
|
// Now let others know that we finished the task. We do this
|
||||||
// by clearing out the state of this task
|
// by clearing out the state of this task
|
||||||
@ -198,6 +218,7 @@ namespace dlib
|
|||||||
tasks[idx].mfp2.clear();
|
tasks[idx].mfp2.clear();
|
||||||
tasks[idx].arg1 = 0;
|
tasks[idx].arg1 = 0;
|
||||||
tasks[idx].arg2 = 0;
|
tasks[idx].arg2 = 0;
|
||||||
|
tasks[idx].eptr = eptr;
|
||||||
task_done_signaler.broadcast();
|
task_done_signaler.broadcast();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -210,6 +231,9 @@ namespace dlib
|
|||||||
find_empty_task_slot (
|
find_empty_task_slot (
|
||||||
) const
|
) const
|
||||||
{
|
{
|
||||||
|
for (auto&& task : tasks)
|
||||||
|
task.propagate_exception();
|
||||||
|
|
||||||
for (unsigned long i = 0; i < tasks.size(); ++i)
|
for (unsigned long i = 0; i < tasks.size(); ++i)
|
||||||
{
|
{
|
||||||
if (tasks[i].is_empty())
|
if (tasks[i].is_empty())
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
#include "../array.h"
|
#include "../array.h"
|
||||||
#include "../smart_pointers_thread_safe.h"
|
#include "../smart_pointers_thread_safe.h"
|
||||||
#include "../smart_pointers.h"
|
#include "../smart_pointers.h"
|
||||||
|
#include <exception>
|
||||||
|
|
||||||
namespace dlib
|
namespace dlib
|
||||||
{
|
{
|
||||||
@ -451,6 +452,17 @@ namespace dlib
|
|||||||
bfp_type bfp;
|
bfp_type bfp;
|
||||||
|
|
||||||
shared_ptr<function_object_copy> function_copy;
|
shared_ptr<function_object_copy> function_copy;
|
||||||
|
mutable std::exception_ptr eptr; // non-null if the task threw an exception
|
||||||
|
|
||||||
|
void propagate_exception() const
|
||||||
|
{
|
||||||
|
if (eptr)
|
||||||
|
{
|
||||||
|
auto tmp = eptr;
|
||||||
|
eptr = nullptr;
|
||||||
|
std::rethrow_exception(tmp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -225,9 +225,11 @@ namespace dlib
|
|||||||
such as mutex objects.
|
such as mutex objects.
|
||||||
|
|
||||||
EXCEPTIONS
|
EXCEPTIONS
|
||||||
Note that if an exception is thrown inside a task thread and
|
Note that if an exception is thrown inside a task thread and is not caught
|
||||||
is not caught then the normal rule for uncaught exceptions in
|
then the exception will be trapped inside the thread pool and rethrown at a
|
||||||
threads applies. That is, the application will be terminated.
|
later time when someone calls one of the add task or wait member functions
|
||||||
|
of the thread pool. This allows exceptions to propagate out of task threads
|
||||||
|
and into the calling code where they can be handled.
|
||||||
!*/
|
!*/
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
@ -5,8 +5,8 @@
|
|||||||
object from the dlib C++ Library.
|
object from the dlib C++ Library.
|
||||||
|
|
||||||
|
|
||||||
This is a very simple example. It creates a thread pool with 3
|
In this example we will crate a thread pool with 3 threads and then show a
|
||||||
threads and then sends a few simple tasks to the pool.
|
few different ways to send tasks to the pool.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
@ -17,18 +17,19 @@
|
|||||||
|
|
||||||
using namespace dlib;
|
using namespace dlib;
|
||||||
|
|
||||||
// We will be using the dlib logger object to print out messages in this example
|
// We will be using the dlib logger object to print messages in this example
|
||||||
// because its output is timestamped and labeled with the thread that the log
|
// 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
|
// message came from. This will make it easier to see what is going on in this
|
||||||
// this example. Here we make an instance of the logger. See the logger
|
// example. Here we make an instance of the logger. See the logger
|
||||||
// documentation and examples for detailed information regarding its use.
|
// documentation and examples for detailed information regarding its use.
|
||||||
logger dlog("main");
|
logger dlog("main");
|
||||||
|
|
||||||
|
|
||||||
// Here we make an instance of the thread pool object
|
// Here we make an instance of the thread pool object. You could also use the
|
||||||
|
// global dlib::default_thread_pool(), which automatically selects the number of
|
||||||
|
// threads based on your hardware. But here let's make our own.
|
||||||
thread_pool tp(3);
|
thread_pool tp(3);
|
||||||
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------------------
|
||||||
|
|
||||||
class test
|
class test
|
||||||
@ -37,27 +38,27 @@ class test
|
|||||||
The thread_pool accepts "tasks" from the user and schedules them for
|
The thread_pool accepts "tasks" from the user and schedules them for
|
||||||
execution in one of its threads when one becomes available. Each task
|
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
|
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
|
test with a few member functions, which we will have the thread pool call
|
||||||
as tasks.
|
as tasks.
|
||||||
*/
|
*/
|
||||||
public:
|
public:
|
||||||
|
|
||||||
void task()
|
void mytask()
|
||||||
{
|
{
|
||||||
dlog << LINFO << "task start";
|
dlog << LINFO << "mytask start";
|
||||||
|
|
||||||
future<int> var;
|
dlib::future<int> var;
|
||||||
|
|
||||||
var = 1;
|
var = 1;
|
||||||
|
|
||||||
// Here we ask the thread pool to call this->subtask() and this->subtask2().
|
// 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
|
// 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
|
// available thread. However, if there isn't a thread ready then
|
||||||
// thread ready then add_task() blocks until there is such a thread.
|
// add_task() blocks until there is such a thread. Also, note that if
|
||||||
// Also note that since task() is executed within the thread pool (see main() below)
|
// mytask() is executed within the thread pool then calls to add_task()
|
||||||
// calls to add_task() will execute the requested task within the calling thread
|
// will execute the requested task within the calling thread in cases
|
||||||
// in cases where the thread pool is full. This means it is always safe to
|
// where the thread pool is full. This means it is always safe to spawn
|
||||||
// spawn subtasks from within another task, which is what we are doing here.
|
// 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::subtask,var); // schedule call to this->subtask(var)
|
||||||
tp.add_task(*this,&test::subtask2); // schedule call to this->subtask2()
|
tp.add_task(*this,&test::subtask2); // schedule call to this->subtask2()
|
||||||
|
|
||||||
@ -66,17 +67,16 @@ public:
|
|||||||
// return the integer it contains. In this case result will be assigned
|
// return the integer it contains. In this case result will be assigned
|
||||||
// the value 2 since var was incremented by subtask().
|
// the value 2 since var was incremented by subtask().
|
||||||
int result = var;
|
int result = var;
|
||||||
// print out the result
|
|
||||||
dlog << LINFO << "var = " << result;
|
dlog << LINFO << "var = " << result;
|
||||||
|
|
||||||
// Wait for all the tasks we have started to finish. Note that
|
// Wait for all the tasks we have started to finish. Note that
|
||||||
// wait_for_all_tasks() only waits for tasks which were started
|
// wait_for_all_tasks() only waits for tasks which were started by the
|
||||||
// by the calling thread. So you don't have to worry about other
|
// calling thread. So you don't have to worry about other unrelated
|
||||||
// unrelated parts of your application interfering. In this case
|
// parts of your application interfering. In this case it just waits
|
||||||
// it just waits for subtask2() to finish.
|
// for subtask2() to finish.
|
||||||
tp.wait_for_all_tasks();
|
tp.wait_for_all_tasks();
|
||||||
|
|
||||||
dlog << LINFO << "task end" ;
|
dlog << LINFO << "mytask end" ;
|
||||||
}
|
}
|
||||||
|
|
||||||
void subtask(int& a)
|
void subtask(int& a)
|
||||||
@ -96,23 +96,7 @@ public:
|
|||||||
|
|
||||||
// ----------------------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------------------
|
||||||
|
|
||||||
class add_value
|
int main() try
|
||||||
{
|
|
||||||
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
|
// tell the logger to print out everything
|
||||||
dlog.set_level(LALL);
|
dlog.set_level(LALL);
|
||||||
@ -120,84 +104,80 @@ int main()
|
|||||||
|
|
||||||
dlog << LINFO << "schedule a few tasks";
|
dlog << LINFO << "schedule a few tasks";
|
||||||
|
|
||||||
test mytask;
|
test taskobj;
|
||||||
// Schedule the thread pool to call mytask.task(). Note that all forms of add_task()
|
// Schedule the thread pool to call taskobj.mytask(). Note that all forms of
|
||||||
// pass in the task object by reference. This means you must make sure, in this case,
|
// add_task() pass in the task object by reference. This means you must make sure,
|
||||||
// that mytask isn't destructed until after the task has finished executing.
|
// in this case, that taskobj isn't destructed until after the task has finished
|
||||||
tp.add_task(mytask, &test::task);
|
// executing.
|
||||||
|
tp.add_task(taskobj, &test::mytask);
|
||||||
|
|
||||||
// You can also pass task objects to a thread pool by value. So in this case we don't
|
// This behavior of add_task() enables it to guarantee that no memory allocations
|
||||||
// have to worry about keeping our own instance of the task. Here we construct a temporary
|
// occur after the thread_pool has been constructed, so long as the user doesn't
|
||||||
// add_value object and pass it right in and everything works like it should.
|
// call any of the add_task_by_value() routines. The future object also doesn't
|
||||||
future<int> num = 3;
|
// perform any memory allocations or contain any system resources such as mutex
|
||||||
tp.add_task_by_value(add_value(7), num); // adds 7 to num
|
// objects. If you don't care about memory allocations then you will likely find
|
||||||
|
// the add_task_by_value() interface more convenient to use, which is shown below.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// If we call add_task_by_value() we 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 create a lambda function and pass it right in and everything
|
||||||
|
// works like it should.
|
||||||
|
dlib::future<int> num = 3;
|
||||||
|
tp.add_task_by_value([](int& val){val += 7;}, num); // adds 7 to num
|
||||||
int result = num.get();
|
int result = num.get();
|
||||||
dlog << LINFO << "result = " << result; // prints result = 10
|
dlog << LINFO << "result = " << result; // prints result = 10
|
||||||
|
|
||||||
|
|
||||||
|
// dlib also contains dlib::async(), which is essentially identical to std::async()
|
||||||
|
// except that it launches tasks to a dlib::thread_pool (using add_task_by_value)
|
||||||
|
// rather than starting an unbounded number of threads. As an example, here we
|
||||||
// uncomment this line if your compiler supports the new C++0x lambda functions
|
// make 10 different tasks, each assigns a different value into the elements of the
|
||||||
//#define COMPILER_SUPPORTS_CPP0X_LAMBDA_FUNCTIONS
|
// vector vect.
|
||||||
#ifdef COMPILER_SUPPORTS_CPP0X_LAMBDA_FUNCTIONS
|
std::vector<std::future<unsigned long>> vect(10);
|
||||||
|
|
||||||
// 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)
|
for (unsigned long i = 0; i < vect.size(); ++i)
|
||||||
{
|
vect[i] = dlib::async(tp, [i]() { return i*i; });
|
||||||
// Make a lambda function which takes vect by reference and i by value. So what
|
// Print the results
|
||||||
// will happen is each assignment statement will run in a thread in the thread_pool.
|
for (unsigned long i = 0; i < vect.size(); ++i)
|
||||||
tp.add_task_by_value([&vect,i](){
|
dlog << LINFO << "vect["<<i<<"]: " << vect[i].get();
|
||||||
vect[i] = i;
|
|
||||||
});
|
|
||||||
}
|
// Finally, it's usually a good idea to wait for all your tasks to complete.
|
||||||
// Wait for all tasks which were requested by the main thread to complete.
|
// Moreover, if any of your tasks threw an exception then waiting for the tasks
|
||||||
|
// will rethrow the exception in the calling context, allowing you to handle it in
|
||||||
|
// your local thread. Also, if you don't wait for the tasks and there is an
|
||||||
|
// exception and you allow the thread pool to be destructed your program will be
|
||||||
|
// terminated. So don't ignore exceptions :)
|
||||||
tp.wait_for_all_tasks();
|
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):
|
||||||
|
|
||||||
/* A possible run of this program might produce the following output (the first column is
|
0 INFO [0] main: schedule a few tasks
|
||||||
the time the log message occurred and the value in [] is the thread id for the thread
|
0 INFO [1] main: task start
|
||||||
that generated the log message):
|
0 INFO [0] main: result = 10
|
||||||
|
200 INFO [2] main: subtask end
|
||||||
1 INFO [0] main: schedule a few tasks
|
200 INFO [1] main: var = 2
|
||||||
1 INFO [1] main: task start
|
200 INFO [0] main: vect[0]: 0
|
||||||
1 INFO [0] main: result = 10
|
200 INFO [0] main: vect[1]: 1
|
||||||
201 INFO [2] main: subtask end
|
200 INFO [0] main: vect[2]: 4
|
||||||
201 INFO [1] main: var = 2
|
200 INFO [0] main: vect[3]: 9
|
||||||
201 INFO [2] main: A message from a lambda function running in another thread.
|
200 INFO [0] main: vect[4]: 16
|
||||||
301 INFO [3] main: subtask2 end
|
200 INFO [0] main: vect[5]: 25
|
||||||
301 INFO [1] main: task end
|
200 INFO [0] main: vect[6]: 36
|
||||||
301 INFO [0] main: vect[0]: 0
|
200 INFO [0] main: vect[7]: 49
|
||||||
301 INFO [0] main: vect[1]: 1
|
200 INFO [0] main: vect[8]: 64
|
||||||
301 INFO [0] main: vect[2]: 2
|
200 INFO [0] main: vect[9]: 81
|
||||||
301 INFO [0] main: vect[3]: 3
|
300 INFO [3] main: subtask2 end
|
||||||
301 INFO [0] main: vect[4]: 4
|
300 INFO [1] main: task end
|
||||||
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
|
|
||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
|
catch(std::exception& e)
|
||||||
|
{
|
||||||
|
std::cout << e.what() << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user