|
938 | 938 | \label{CONDITION_VARIABLES} |
939 | 939 |
|
940 | 940 | \begin{itemize} |
941 | | -\item in other words -- conditional variables are handy in situation when a |
942 | | -thread needs to test the state of \emsl{shared} data (e.g. number of queues |
943 | | -in queue), and voluntarily put itself to sleep, when desired state has not |
944 | | -been reached. The sleeping thread is then woken up by another thread that |
945 | | -changed the state of the data, so the situation which the first thread was |
946 | | -waiting on actually happened (e.g. by inserting a item into queue). |
947 | | -The second thread wakes the first one by calling a designated function. |
948 | | -If no thread is sleeping at the moment, the function will have no effect -- |
949 | | -nothing will be saved anywhere, like it never happened. |
950 | | -\item A condition variable, which is opaque type for the programmer, is not |
| 941 | +\item In other words -- conditional variables are handy in a situation when a |
| 942 | +thread needs to test the state of \emsl{shared} data (e.g. number of elements in |
| 943 | +a queue) and voluntarily put itself to sleep if the state is not as desired. |
| 944 | +The sleeping thread may be woken up by another thread after the latter |
| 945 | +changed the state of the data in a way that the situation which the first thread |
| 946 | +was waiting on actually happened (e.g. by inserting an item into a queue). The |
| 947 | +second thread wakes the first one by calling a designated function. If no |
| 948 | +thread is sleeping at the moment, that function would have no effect -- nothing |
| 949 | +will be saved anywhere, it is as if it never happened. |
| 950 | +\item A condition variable, which is an opaque type for the programmer, is not |
951 | 951 | associated with a concrete condition like ``\emph{\texttt{n} is greater than |
952 | | -7}''. Conditional variable can be in fact compared to a flag of certain color; |
953 | | -if it is lifted up, it means that the threads waiting for the flag to be waved |
954 | | -are informed (= woken up) and can use this information according its own |
955 | | -judgment. Some threads can wait for \texttt{n} to be bigger than 7, the other |
956 | | -can be waiting solely for \texttt{n} to change anyhow. It is only up to the |
957 | | -programmer, if for concrete situation uses just one condition variable or |
958 | | -for all situations. For the second situation, the threads waiting on |
959 | | -\texttt{n == 7} have to always test \texttt{n}, because they know that they |
960 | | -are woken up whenever the variable changed. If the variable is not equal to 7, |
961 | | -it will voluntarily put itself to sleep. How it is explained further, |
962 | | -the \emsl{test is necessary to do after wake up}, even if dedicated conditional |
963 | | -variable is used -- it can happen that the system can wake up the sleeping |
964 | | -thread (because of various implementation reasons) without being caused by |
965 | | -another thread. |
| 952 | +7}''. A conditional variable may in fact be compared to a flag of a certain |
| 953 | +color; if it is lifted up, it means that the threads waiting for the flag to be |
| 954 | +raised are informed (= woken up) and may use this information to its own |
| 955 | +judgment. Some threads may wait for \texttt{n} to be bigger than 7, some other |
| 956 | +may be waiting solely for \texttt{n} to change, and another then for \texttt{n} |
| 957 | +to become 99. It is only up to the programmer whether a separate condition |
| 958 | +variable will be used for all states of \texttt{n} (i.e. we would use multiple |
| 959 | +flags of different colors) or whether a single condition variable will be used |
| 960 | +(the same flag color for all situations). For the latter, the threads waiting |
| 961 | +on \texttt{n > 7} and \texttt{n == 99} must always test \texttt{n} as they know |
| 962 | +that they are woken up whenever the variable changed. If the variable is not |
| 963 | +equal to 7 the thread must voluntarily put itself to sleep again. As it is |
| 964 | +explained further, the \emsl{test is necessary to perform after an every |
| 965 | +wake-up} even if a dedicated conditional variable is used for every possible |
| 966 | +state -- it may happen that the system can wake up a sleeping thread (because |
| 967 | +of various implementation reasons) without any other thread causing this; it is |
| 968 | +called a \emsl{spurious wakeup}. |
966 | 969 | \end{itemize} |
967 | 970 |
|
968 | 971 | %%%%% |
969 | 972 |
|
970 | | -%%%%% |
971 | | - |
972 | 973 | ifdef([[[NOSPELLCHECK]]], [[[ |
973 | 974 | \pdfbookmark[1]{pthread\_cond\_init, pthread\_cond\_destroy,% |
974 | 975 | pthread\_cond\_wait}{pthreadcondvarfncs} |
|
1005 | 1006 | \end{slide} |
1006 | 1007 |
|
1007 | 1008 | \begin{itemize} |
| 1009 | +\item While the condition variables are used for putting threads to sleep and |
| 1010 | +waking them up, given that we work with shared data, there is always a mutex |
| 1011 | +involved when working with a condition variable. |
1008 | 1012 | \item It is necessary to test the condition after the thread locks the mutex |
1009 | 1013 | and before the \texttt{pthread\_cond\_wait} is called. If the thread does not |
1010 | 1014 | perform this operation, it could be put to sleep indefinitely because the |
|
1017 | 1021 | \item The conditional variables API works thanks to the fact that when entering |
1018 | 1022 | critical section the mutex is locked by the thread and the |
1019 | 1023 | \emsl{\texttt{pthread\_cond\_wait} function will unlock the mutex before putting |
1020 | | -the thread to sleep}. Before exiting from the function the mutex is again |
1021 | | -locked. |
1022 | | -It can therefore happen that the thread is woken up while waiting on a |
1023 | | -conditional variable and then sleeps again when waiting for the mutex. |
1024 | | -There is nothing complicated about this, it is merely mutual exclusion of |
1025 | | -threads in critical section. |
| 1024 | +the thread to sleep}. Before exiting from the function the mutex is locked |
| 1025 | +again. It may therefore happen that the thread is woken up while waiting on a |
| 1026 | +conditional variable and then put to sleep again when hitting a mutex already |
| 1027 | +locked by another thread. There is nothing complicated about this, it is merely |
| 1028 | +a mutual exclusion of threads in a critical section. |
1026 | 1029 | \end{itemize} |
1027 | 1030 |
|
1028 | 1031 | %%%%% |
|
1061 | 1064 | \end{slide} |
1062 | 1065 |
|
1063 | 1066 | \begin{itemize} |
1064 | | -\item one conditional variable can be used to announce multiple situations |
1065 | | -at once -- e.g. when inserting or removing an item to/from a queue. |
1066 | | -Because of this, it is necessary to test the condition the thread is waiting |
1067 | | -for. Another consequence of this is that it is necessary to use broadcast |
1068 | | -in such situation. Let's assume that both readers and writers are waiting for |
1069 | | -condition ``state of queue has changed''. If only single wake-up event is made |
1070 | | -after item insertion to the queue, then another writer can be woken up, |
1071 | | -which is however waiting for different event -- item removal. Thus, the message |
1072 | | -will remain in the queue until a reader is woken up. |
1073 | | -\item A thread can be woken up by another thread also in case when a condition |
1074 | | -variable is associated with concrete event, which is no longer true after |
1075 | | -the waiting thread is woken up. |
1076 | | -Let's consider this situation: a thread signals a condition change and right |
1077 | | -after that another thread locks the mutex and performs an action which |
1078 | | -invalidates the condition, e.g. removing a item from a queue when the event was |
1079 | | -``there is a messages in the queue''. Thus the thread which is woken up |
1080 | | -finds the queue empty. This is another reason why the condition variable |
1081 | | -\emsl{always} has to be tested in a cycle. |
| 1067 | +\item One conditional variable can be used to announce multiple situations at |
| 1068 | +once -- e.g. when inserting or removing an item to/from a queue. Because of |
| 1069 | +this, it is necessary to test the condition the thread is waiting for. Another |
| 1070 | +consequence of this is that it is necessary to use a broadcast in such a |
| 1071 | +situation. Let us assume that both readers and writers are waiting for a |
| 1072 | +condition ``state of the queue has changed''. If only a single wake-up event is |
| 1073 | +made after an item insertion to the queue, a writer may be woken up but it is |
| 1074 | +however waiting for a different event -- an item removal, so it puts itself to |
| 1075 | +sleep again. Thus, the message will remain in the queue until a reader is woken |
| 1076 | +up. |
| 1077 | +\item A thread may be woken up by another thread even in a case when a condition |
| 1078 | +variable is associated with a specific event that is no longer true after the |
| 1079 | +waiting thread is woken up. Let's consider this situation: a thread signals a |
| 1080 | +condition change and right after that another thread locks the mutex and |
| 1081 | +performs an action which invalidates the condition, e.g. removing an item from a |
| 1082 | +queue while the event was ``there is an item in the queue''. So, the thread |
| 1083 | +woken up finds the queue empty. That is another reason why the condition the |
| 1084 | +thread is waiting for must be \emsl{always} tested in a cycle. |
1082 | 1085 | \item It is also possible that a thread is woken up and the condition is not |
1083 | | -true due to concrete implementation. That's another reason to use the cycle. |
| 1086 | +true due to a spurious wake-up already mentioned in a previous page. So again, |
| 1087 | +the loop must be used. |
1084 | 1088 | \item The \texttt{abstime} parameter of the \texttt{pthread\_cond\_timedwait} |
1085 | | -function is absolute time, i.e. the timeout expires when system time reaches |
1086 | | -the value greater or equal to \texttt{abstime}. The absolute time is used |
1087 | | -so that it is not necessary to recompute the time difference after wake-up |
1088 | | -events. |
| 1089 | +function is absolute time, i.e. the timeout expires when the system time reaches |
| 1090 | +the value greater or equal to \texttt{abstime}. The absolute time is used so |
| 1091 | +that it is not necessary to recompute the time difference after wake-up events. |
1089 | 1092 | \end{itemize} |
1090 | 1093 |
|
1091 | 1094 | %%%%% |
|
1111 | 1114 |
|
1112 | 1115 | \begin{itemize} |
1113 | 1116 | \prgchars |
1114 | | -\item The first piece of code is waiting for the condition to change. |
1115 | | -If this happens, the data changed and can therefore be processed. |
1116 | | -The second piece of code (executed in different thread) prepares the |
1117 | | -data so it can be processed. Once ready, it will signal the consumer. |
1118 | | -\item The \texttt{pthread\_cond\_wait} function will automatically unlock mutex |
1119 | | -and put the thread to sleep. Once the thread is woken up, it will lock the |
1120 | | -mutex first (this will be done by the condition variables implementation) |
1121 | | -and only after that \texttt{pthread\_cond\_wait} will return. |
| 1117 | +\item The first piece of code is waiting for the condition to change. If this |
| 1118 | +happens, the data changed and can therefore be processed. The second piece of |
| 1119 | +code (executed in a different thread) prepares the data so it can be processed. |
| 1120 | +Once ready, it will signal the consumer. \item The \texttt{pthread\_cond\_wait} |
| 1121 | +function will automatically unlock the mutex and put the thread to sleep. Once |
| 1122 | +the thread is woken up, the system will lock the mutex first (this will be done |
| 1123 | +by the condition variable implementation) and only after that |
| 1124 | +\texttt{pthread\_cond\_wait} will return. |
1122 | 1125 | \item When signalling that something has changed, it does not mean that after |
1123 | 1126 | the change the condition will be true. Moreover, |
1124 | 1127 | \texttt{pthread\_cond\_wait} can return even if no thread called |
|
1127 | 1130 | going to sleep again. |
1128 | 1131 | \item The mutex in the example above is unlocked only after the condition |
1129 | 1132 | was signalled however it is not necessary. The signalling can be done |
1130 | | -after unlocking and in such case it can be more efficient (depending on |
1131 | | -given implementation) because the thread that has been woken up will not |
1132 | | -get blocked by the mutex which is held when signalling in critical section. |
| 1133 | +after unlocking and in such a case it can be more efficient (depending on |
| 1134 | +a given implementation) because the thread that has been woken up will not get |
| 1135 | +immediatelly blocked by the mutex which is still held by the thread signalling |
| 1136 | +from within the critical section. |
1133 | 1137 | \item \label{QUEUESIMULATION} Example: |
1134 | 1138 | \example{cond-variables/queue-simulation.c} |
1135 | 1139 | \end{itemize} |
|
0 commit comments