"docs/vscode:/vscode.git/clone" did not exist on "6e68c71503682c8693cb5b06a4da4911dfd655ee"
thread_pool_ex.cpp 6.22 KB
Newer Older
1
// The contents of this file are in the public domain. See LICENSE_FOR_EXAMPLE_PROGRAMS.txt
Davis King's avatar
Davis King committed
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/*

    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"

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");

26
27
28
29
30

// Here we make an instance of the thread pool object
thread_pool tp(3);


Davis King's avatar
Davis King committed
31
32
33
34
35
36
37
// ----------------------------------------------------------------------------------------

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
38
39
        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).
Davis King's avatar
Davis King committed
40
41
42
43
44
45
46
47
48
49
50
        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()
    {
        dlog << LINFO << "task_0 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 
51
        // immediately if there is an available thread to hand the task off to.  However,
Davis King's avatar
Davis King committed
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
        // 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)
        // 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.  
        tp.wait_for_all_tasks();

        dlog << LINFO << "task_0 end" ;
    }

    void subtask(long a)
    {
        dlib::sleep(200);
        dlog << LINFO << "subtask end " << a;
    }

    void task_1(long a, long b)
    {
        dlog << LINFO << "task_1 start: " << a << ", " << b;
        dlib::sleep(700);
        dlog << LINFO << "task_1 end: " << a << ", " << b;
    }

};

// ----------------------------------------------------------------------------------------

86
87
88
89
90
91
92
93
94
95
96
97
void add (
    long a,
    long b,
    long& result
)
{
    dlib::sleep(400);
    result = a + b;
}

// ----------------------------------------------------------------------------------------

Davis King's avatar
Davis King committed
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
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);

    // 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.
    tp.wait_for_all_tasks();
    dlog << LINFO << "all tasks finished";


132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155

    // 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";



Davis King's avatar
Davis King committed
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
    /* 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):

    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
176
177
178
179
 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
Davis King's avatar
Davis King committed
180
181
182
183
184
185
186
    */
}