1 // The contents of this file are in the public domain. See LICENSE_FOR_EXAMPLE_PROGRAMS.txt
2 
3 /*
4 
5     This is an example illustrating the use of the threading api from the dlib
6     C++ Library.
7 
8 
9     This is a very simple example.  It makes some threads and just waits for
10     them to terminate.  It should be noted that this example shows how to use
11     the lowest level of the dlib threading API.  Often, other higher level tools
12     are more appropriate.  For examples of higher level tools see the
13     documentation on the pipe, thread_pool, thread_function, or
14     threaded_object.
15 */
16 
17 
18 #include <iostream>
19 #include <dlib/threads.h>
20 #include <dlib/misc_api.h>  // for dlib::sleep
21 
22 using namespace std;
23 using namespace dlib;
24 
25 int thread_count = 10;
26 dlib::mutex count_mutex; // This is a mutex we will use to guard the thread_count variable.  Note that the mutex doesn't know
27                    // anything about the thread_count variable.  Only our usage of a mutex determines what it guards.
28                    // In this case we are going to make sure this mutex is always locked before we touch the
29                    // thread_count variable.
30 
31 signaler count_signaler(count_mutex);  // This is a signaler we will use to signal when
32                                        // the thread_count variable is changed.  Note that it is
33                                        // associated with the count_mutex.  This means that
34                                        // when you call count_signaler.wait() it will automatically
35                                        // unlock count_mutex for you.
36 
37 
test_thread(void *)38 void test_thread (void*)
39 {
40     // just sleep for a second
41     dlib::sleep(1000);
42 
43     // Now signal that this thread is ending.  First we should get a lock on the
44     // count_mutex so we can safely mess with thread_count.  A convenient way to do this
45     // is to use an auto_mutex object.  Its constructor takes a mutex object and locks
46     // it right away, it then unlocks the mutex when the auto_mutex object is destructed.
47     // Note that this happens even if an exception is thrown.  So it ensures that you
48     // don't somehow quit your function without unlocking your mutex.
49     auto_mutex locker(count_mutex);
50     --thread_count;
51     // Now we signal this change.  This will cause one thread that is currently waiting
52     // on a call to count_signaler.wait() to unblock.
53     count_signaler.signal();
54 
55     // At the end of this function locker goes out of scope and gets destructed, thus
56     // unlocking count_mutex for us.
57 }
58 
main()59 int main()
60 {
61 
62     cout << "Create some threads" << endl;
63     for (int i = 0; i < thread_count; ++i)
64     {
65         // Create some threads.  This 0 we are passing in here is the argument that gets
66         // passed to the thread function (a void pointer) but we aren't using it in this
67         // example program so i'm just using 0.
68         create_new_thread(test_thread,0);
69     }
70     cout << "Done creating threads, now we wait for them to end" << endl;
71 
72 
73     // Again we use an auto_mutex to get a lock.  We don't have to do it this way
74     // but it is convenient.  Also note that we can name the auto_mutex object anything.
75     auto_mutex some_random_unused_name(count_mutex);
76 
77     // Now we wait in a loop for thread_count to be 0.  Note that it is important to do this in a
78     // loop because it is possible to get spurious wakeups from calls to wait() on some
79     // platforms.  So this guards against that and it also makes the code easy to understand.
80     while (thread_count > 0)
81         count_signaler.wait(); // This puts this thread to sleep until we get a signal to look at the
82                                // thread_count variable.  It also unlocks the count_mutex before it
83                                // goes to sleep and then relocks it when it wakes back up.  Again,
84                                // note that it is possible for wait() to return even if no one signals you.
85                                // This is just weird junk you have to deal with on some platforms.  So
86                                // don't try to be clever and write code that depends on the number of
87                                // times wait() returns because it won't always work.
88 
89 
90     cout << "All threads done, ending program" << endl;
91 }
92 
93 
94