1 | <?xml version="1.0" encoding="utf-8"?> |
---|
2 | <!DOCTYPE library PUBLIC "-//Boost//DTD BoostBook XML V1.0//EN" |
---|
3 | "http://www.boost.org/tools/boostbook/dtd/boostbook.dtd" [ |
---|
4 | <!ENTITY % threads.entities SYSTEM "entities.xml"> |
---|
5 | %threads.entities; |
---|
6 | ]> |
---|
7 | <section id="threads.faq" last-revision="$Date: 2004/07/17 04:33:59 $"> |
---|
8 | <title>Frequently Asked Questions</title> |
---|
9 | <qandaset> |
---|
10 | <qandaentry> |
---|
11 | <question> |
---|
12 | <para>Are lock objects <link |
---|
13 | linkend="threads.glossary.thread-safe">thread safe</link>?</para> |
---|
14 | </question> |
---|
15 | <answer> |
---|
16 | <para><emphasis role="bold">No!</emphasis> Lock objects are not meant to |
---|
17 | be shared between threads. They are meant to be short-lived objects |
---|
18 | created on automatic storage within a code block. Any other usage is |
---|
19 | just likely to lead to errors and won't really be of actual benefit anyway. |
---|
20 | Share <link linkend="threads.concepts.mutexes">Mutexes</link>, not |
---|
21 | Locks. For more information see the <link |
---|
22 | linkend="threads.rationale.locks">rationale</link> behind the |
---|
23 | design for lock objects.</para> |
---|
24 | </answer> |
---|
25 | </qandaentry> |
---|
26 | <qandaentry> |
---|
27 | <question> |
---|
28 | <para>Why was &Boost.Threads; modeled after (specific library |
---|
29 | name)?</para> |
---|
30 | </question> |
---|
31 | <answer> |
---|
32 | <para>It wasn't. &Boost.Threads; was designed from scratch. Extensive |
---|
33 | design discussions involved numerous people representing a wide range of |
---|
34 | experience across many platforms. To ensure portability, the initial |
---|
35 | implements were done in parallel using POSIX Threads and the Win32 |
---|
36 | threading API. But the &Boost.Threads; design is very much in the spirit |
---|
37 | of C++, and thus doesn't model such C based APIs.</para> |
---|
38 | </answer> |
---|
39 | </qandaentry> |
---|
40 | <qandaentry> |
---|
41 | <question> |
---|
42 | <para>Why wasn't &Boost.Threads; modeled after (specific library |
---|
43 | name)?</para> |
---|
44 | </question> |
---|
45 | <answer> |
---|
46 | <para>Existing C++ libraries either seemed dangerous (often failing to |
---|
47 | take advantage of prior art to reduce errors) or had excessive |
---|
48 | dependencies on library components unrelated to threading. Existing C |
---|
49 | libraries couldn't meet our C++ requirements, and were also missing |
---|
50 | certain features. For instance, the WIN32 thread API lacks condition |
---|
51 | variables, even though these are critical for the important Monitor |
---|
52 | pattern &cite.SchmidtStalRohnertBuschmann;.</para> |
---|
53 | </answer> |
---|
54 | </qandaentry> |
---|
55 | <qandaentry> |
---|
56 | <question> |
---|
57 | <para>Why do <link linkend="threads.concepts.mutexes">Mutexes</link> |
---|
58 | have noncopyable semantics?</para> |
---|
59 | </question> |
---|
60 | <answer> |
---|
61 | <para>To ensure that <link |
---|
62 | linkend="threads.glossary.deadlock">deadlocks</link> don't occur. The |
---|
63 | only logical form of copy would be to use some sort of shallow copy |
---|
64 | semantics in which multiple mutex objects could refer to the same mutex |
---|
65 | state. This means that if ObjA has a mutex object as part of its state |
---|
66 | and ObjB is copy constructed from it, then when ObjB::foo() locks the |
---|
67 | mutex it has effectively locked ObjA as well. This behavior can result |
---|
68 | in deadlock. Other copy semantics result in similar problems (if you |
---|
69 | think you can prove this to be wrong then supply us with an alternative |
---|
70 | and we'll reconsider).</para> |
---|
71 | </answer> |
---|
72 | </qandaentry> |
---|
73 | <qandaentry> |
---|
74 | <question> |
---|
75 | <para>How can you prevent <link |
---|
76 | linkend="threads.glossary.deadlock">deadlock</link> from occurring when |
---|
77 | a thread must lock multiple mutexes?</para> |
---|
78 | </question> |
---|
79 | <answer> |
---|
80 | <para>Always lock them in the same order. One easy way of doing this is |
---|
81 | to use each mutex's address to determine the order in which they are |
---|
82 | locked. A future &Boost.Threads; concept may wrap this pattern up in a |
---|
83 | reusable class.</para> |
---|
84 | </answer> |
---|
85 | </qandaentry> |
---|
86 | <qandaentry> |
---|
87 | <question> |
---|
88 | <para>Don't noncopyable <link |
---|
89 | linkend="threads.concepts.mutexes">Mutex</link> semantics mean that a |
---|
90 | class with a mutex member will be noncopyable as well?</para> |
---|
91 | </question> |
---|
92 | <answer> |
---|
93 | <para>No, but what it does mean is that the compiler can't generate a |
---|
94 | copy constructor and assignment operator, so they will have to be coded |
---|
95 | explicitly. This is a <emphasis role="bold">good thing</emphasis>, |
---|
96 | however, since the compiler generated operations would not be <link |
---|
97 | linkend="threads.glossary.thread-safe">thread-safe</link>. The following |
---|
98 | is a simple example of a class with copyable semantics and internal |
---|
99 | synchronization through a mutex member.</para> |
---|
100 | <programlisting> |
---|
101 | class counter |
---|
102 | { |
---|
103 | public: |
---|
104 | // Doesn't need synchronization since there can be no references to *this |
---|
105 | // until after it's constructed! |
---|
106 | explicit counter(int initial_value) |
---|
107 | : m_value(initial_value) |
---|
108 | { |
---|
109 | } |
---|
110 | // We only need to synchronize other for the same reason we don't have to |
---|
111 | // synchronize on construction! |
---|
112 | counter(const counter& other) |
---|
113 | { |
---|
114 | boost::mutex::scoped_lock scoped_lock(other.m_mutex); |
---|
115 | m_value = other.m_value; |
---|
116 | } |
---|
117 | // For assignment we need to synchronize both objects! |
---|
118 | const counter& operator=(const counter& other) |
---|
119 | { |
---|
120 | if (this == &other) |
---|
121 | return *this; |
---|
122 | boost::mutex::scoped_lock lock1(&m_mutex < &other.m_mutex ? m_mutex : other.m_mutex); |
---|
123 | boost::mutex::scoped_lock lock2(&m_mutex > &other.m_mutex ? m_mutex : other.m_mutex); |
---|
124 | m_value = other.m_value; |
---|
125 | return *this; |
---|
126 | } |
---|
127 | int value() const |
---|
128 | { |
---|
129 | boost::mutex::scoped_lock scoped_lock(m_mutex); |
---|
130 | return m_value; |
---|
131 | } |
---|
132 | int increment() |
---|
133 | { |
---|
134 | boost::mutex::scoped_lock scoped_lock(m_mutex); |
---|
135 | return ++m_value; |
---|
136 | } |
---|
137 | private: |
---|
138 | mutable boost::mutex m_mutex; |
---|
139 | int m_value; |
---|
140 | }; |
---|
141 | </programlisting> |
---|
142 | </answer> |
---|
143 | </qandaentry> |
---|
144 | <qandaentry> |
---|
145 | <question> |
---|
146 | <para>How can you lock a <link |
---|
147 | linkend="threads.concepts.mutexes">Mutex</link> member in a const member |
---|
148 | function, in order to implement the Monitor Pattern?</para> |
---|
149 | </question> |
---|
150 | <answer> |
---|
151 | <para>The Monitor Pattern &cite.SchmidtStalRohnertBuschmann; mutex |
---|
152 | should simply be declared as mutable. See the example code above. The |
---|
153 | internal state of mutex types could have been made mutable, with all |
---|
154 | lock calls made via const functions, but this does a poor job of |
---|
155 | documenting the actual semantics (and in fact would be incorrect since |
---|
156 | the logical state of a locked mutex clearly differs from the logical |
---|
157 | state of an unlocked mutex). Declaring a mutex member as mutable clearly |
---|
158 | documents the intended semantics.</para> |
---|
159 | </answer> |
---|
160 | </qandaentry> |
---|
161 | <qandaentry> |
---|
162 | <question> |
---|
163 | <para>Why supply <classname>boost::condition</classname> variables rather than |
---|
164 | event variables?</para> |
---|
165 | </question> |
---|
166 | <answer> |
---|
167 | <para>Condition variables result in user code much less prone to <link |
---|
168 | linkend="threads.glossary.race-condition">race conditions</link> than |
---|
169 | event variables. See <xref linkend="threads.rationale.events" /> |
---|
170 | for analysis. Also see &cite.Hoare74; and &cite.SchmidtStalRohnertBuschmann;. |
---|
171 | </para> |
---|
172 | </answer> |
---|
173 | </qandaentry> |
---|
174 | <qandaentry> |
---|
175 | <question> |
---|
176 | <para>Why isn't thread cancellation or termination provided?</para> |
---|
177 | </question> |
---|
178 | <answer> |
---|
179 | <para>There's a valid need for thread termination, so at some point |
---|
180 | &Boost.Threads; probably will include it, but only after we can find a |
---|
181 | truly safe (and portable) mechanism for this concept.</para> |
---|
182 | </answer> |
---|
183 | </qandaentry> |
---|
184 | <qandaentry> |
---|
185 | <question> |
---|
186 | <para>Is it safe for threads to share automatic storage duration (stack) |
---|
187 | objects via pointers or references?</para> |
---|
188 | </question> |
---|
189 | <answer> |
---|
190 | <para>Only if you can guarantee that the lifetime of the stack object |
---|
191 | will not end while other threads might still access the object. Thus the |
---|
192 | safest practice is to avoid sharing stack objects, particularly in |
---|
193 | designs where threads are created and destroyed dynamically. Restrict |
---|
194 | sharing of stack objects to simple designs with very clear and |
---|
195 | unchanging function and thread lifetimes. (Suggested by Darryl |
---|
196 | Green).</para> |
---|
197 | </answer> |
---|
198 | </qandaentry> |
---|
199 | <qandaentry> |
---|
200 | <question> |
---|
201 | <para>Why has class semaphore disappeared?</para> |
---|
202 | </question> |
---|
203 | <answer> |
---|
204 | <para>Semaphore was removed as too error prone. The same effect can be |
---|
205 | achieved with greater safety by the combination of a mutex and a |
---|
206 | condition variable.</para> |
---|
207 | </answer> |
---|
208 | </qandaentry> |
---|
209 | </qandaset> |
---|
210 | </section> |
---|