Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in
Toggle navigation
D
dlib
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
钟尚武
dlib
Commits
b6ae2a6d
Commit
b6ae2a6d
authored
Jun 08, 2011
by
Davis King
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Improved the thread_pool example program.
parent
cf8e3d6f
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
111 additions
and
94 deletions
+111
-94
thread_pool_ex.cpp
examples/thread_pool_ex.cpp
+111
-94
No files found.
examples/thread_pool_ex.cpp
View file @
b6ae2a6d
...
...
@@ -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)
// 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.
// 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 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
);
//
schedule the thread pool to call a.task_0().
uint64
id
=
tp
.
add_task
(
a
,
&
test
::
task_0
);
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 a call to a.task_1(12,13)
tp
.
add_task
(
a
,
&
test
::
task_1
,
12
,
13
);
// 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
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.
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
);
// 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
// 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
;
// 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.
// We can also use futures with member functions like so:
tp
.
add_task
(
a
,
&
test
::
task_1
,
n1
,
n2
);
// 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."
;
});
// and we can still wait for tasks like so:
// 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 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
*/
}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment