|
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