|
2 | 2 |
|
3 | 3 | \begin{frame}[fragile] |
4 | 4 | \frametitlecpp[11]{Condition variables} |
5 | | - \begin{block}{Communicating thread dependencies} |
| 5 | + \begin{block}{Communicating between threads} |
6 | 6 | \begin{itemize} |
7 | | - \item \mintinline{cpp}{std::condition_variable} from \mintinline{cpp}{<condition_variable>} header |
| 7 | + \item Take the case where threads are waiting for other thread(s) |
| 8 | + \item \mintinline{cpp}{std::condition_variable} |
| 9 | + \begin{itemize} |
| 10 | + \item from \mintinline{cpp}{<condition_variable>} header |
| 11 | + \end{itemize} |
8 | 12 | \item Allows for a thread to sleep (= conserve CPU time) until a given condition is satisfied |
9 | 13 | \end{itemize} |
10 | 14 | \end{block} |
|
23 | 27 | \end{frame} |
24 | 28 |
|
25 | 29 | \begin{frame}[fragile] |
26 | | - \frametitlecpp[17]{Using condition variables} |
27 | | - \begin{block}{Producer side} |
| 30 | + \frametitlecpp[17]{Using condition variables: notify} |
| 31 | + \begin{block}{Producer side: providing data to waiting threads} |
28 | 32 | \begin{itemize} |
29 | | - \item Imagine multiple threads sharing data. Protect it with a mutex |
30 | | - \item Use a condition variable to notify consumers |
31 | | - \item Optimization: Don't hold lock while notifying (would block the waking threads) |
| 33 | + \item Protect data with a mutex, and use condition variable to notify consumers |
| 34 | + \item Optimal use: don't hold lock while notifying |
| 35 | + \begin{itemize} |
| 36 | + \item waiting threads would be blocked |
| 37 | + \end{itemize} |
32 | 38 | \end{itemize} |
33 | 39 | \end{block} |
34 | 40 | \begin{exampleblock}{} |
|
48 | 54 | \end{frame} |
49 | 55 |
|
50 | 56 | \begin{frame}[fragile] |
| 57 | + \frametitlecpp[11]{Using condition variables: wait} |
| 58 | + \vspace{-1.2\baselineskip} |
51 | 59 | \begin{overprint} |
52 | 60 | \onslide<1> |
53 | | - \begin{block}{Consumer side I: Going into wait} |
| 61 | + \begin{block}{Mechanics of wait} |
54 | 62 | \begin{itemize} |
55 | | - \item Start many threads which have to wait for shared data |
56 | | - \item Provide a lock to be managed by \mintinline{cpp}{wait} |
57 | | - \item \mintinline{cpp}{wait} will only lock while necessary; unlocked while sleeping |
58 | | - \item Threads might wake up, but \mintinline{cpp}{wait} returns only when condition satisfied |
| 63 | + \item Many threads are waiting for shared data |
| 64 | + \item Pass a \mintinline{cpp}{unique_lock} and a predicate for wakeup to \mintinline{cpp}{wait()} |
| 65 | + \item \mintinline{cpp}{wait()} sends threads to sleep while predicate is \mintinline{cpp}{false} |
| 66 | + \item \mintinline{cpp}{wait()} will only lock when necessary; unlocked while sleeping |
| 67 | + \item Threads might wake up spuriously, but \mintinline{cpp}{wait()} returns only when lock available \emph{and} predicate \mintinline{cpp}{true} |
59 | 68 | \end{itemize} |
60 | 69 | \end{block} |
61 | 70 | \onslide<2-> |
62 | | - \begin{block}{Consumer side II: Waking up} |
| 71 | + \begin{block}{Waiting / waking up} |
63 | 72 | \begin{itemize} |
64 | 73 | \item \mintinline{cpp}{notify_all()} is called, threads wake up |
65 | | - \item Threads try to acquire mutex, evaluate condition |
66 | | - \item One thread succeeds to acquire mutex, exits from \mintinline{cpp}{wait} |
67 | | - \item \alt<2>{ {\color{red} Problem}: Other threads still blocked!}{ {\color{green!80!black} Solution:} Put locking and waiting in a scope} |
| 74 | + \item Threads try to lock mutex, and evaluate predicate |
| 75 | + \item One thread succeeds to acquire mutex, starts data processing |
| 76 | + \item {\color{red} Problem}: Thread holds mutex now, other threads are blocked! |
68 | 77 | \end{itemize} |
69 | 78 | \end{block} |
70 | 79 | \end{overprint} |
71 | 80 |
|
72 | | - \begin{exampleblock}{} |
73 | | - \begin{overprint} |
74 | | - \onslide<1-2> |
75 | | - \begin{cppcode*}{gobble=2,highlightlines=4} |
| 81 | + \begin{alertblock}{Na\"ive waiting} |
| 82 | + \begin{cppcode*}{gobble=2,highlightlines=3} |
76 | 83 | auto processData = [&](){ |
77 | | - |
78 | 84 | std::unique_lock<std::mutex> lock{mutex}; |
79 | 85 | cond.wait(lock, [&](){ return data.isReady(); }); |
80 | | - |
81 | 86 | process(data); |
82 | 87 | }; |
83 | | - std::thread t1{processData}, t2{processData}, ...; |
84 | | - for (auto t : {&producer, &t1, &t2, ...}) t->join(); |
85 | 88 | \end{cppcode*} |
| 89 | + \end{alertblock} |
| 90 | +\end{frame} |
| 91 | + |
| 92 | +\begin{frame}[fragile] |
| 93 | + \frametitlecpp[11]{Using condition variables: correct wait} |
| 94 | + \begin{block}{Waiting / waking up} |
| 95 | + \begin{itemize} |
| 96 | + \item {\color{green!50!black} Solution:} Put locking and waiting in a scope |
| 97 | + \item Threads will one-by-one wake up, acquire lock, evaluate predicate, release lock |
| 98 | + \end{itemize} |
| 99 | + \end{block} |
86 | 100 |
|
87 | | - \onslide<3> |
| 101 | + \begin{exampleblock}{Correct waiting} |
88 | 102 | \begin{cppcode*}{gobble=2} |
89 | 103 | auto processData = [&](){ |
90 | 104 | { |
|
96 | 110 | std::thread t1{processData}, t2{processData}, ...; |
97 | 111 | for (auto t : {&producer, &t1, &t2, ...}) t->join(); |
98 | 112 | \end{cppcode*} |
99 | | - \end{overprint} |
100 | 113 | \end{exampleblock} |
101 | 114 | \end{frame} |
102 | 115 |
|
|
0 commit comments