问题
An example from The Linux Programming Interface:
In the producer threads, we would have code such as the following:
static pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;
static int avail = 0;
/* Code to produce a unit omitted */
s = pthread_mutex_lock(&mtx);
if (s != 0)
errExitEN(s, "pthread_mutex_lock");
avail++; /* Let consumer know another unit is available */
s = pthread_mutex_unlock(&mtx);
if (s != 0)
errExitEN(s, "pthread_mutex_unlock");
And in the main (consumer) thread, we could employ the following code:
for (;;) {
s = pthread_mutex_lock(&mtx);
if (s != 0)
errExitEN(s, "pthread_mutex_lock");
while (avail > 0) {
/* Consume all available units */
/* Do something with produced unit */
avail--;
}
s = pthread_mutex_unlock(&mtx);
if (s != 0)
errExitEN(s, "pthread_mutex_unlock");
}
The above code works, but it wastes CPU time, because the main thread continually loops, checking the state of the variable
avail
. A condition variable remedies this problem. It allows a thread to sleep (wait) until another thread notifies (signals) it that it must do something.
The book then gives what he thinks is a better version, with condition variable:
static pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
static int avail = 0;
Producer thread:
/* Code to produce a unit omitted */
s = pthread_mutex_lock(&mtx);
if (s != 0)
errExitEN(s, "pthread_mutex_lock");
avail++; /* Let consumer know another unit is available */
s = pthread_mutex_unlock(&mtx);
if (s != 0)
errExitEN(s, "pthread_mutex_unlock");
s = pthread_cond_signal(&cond); /* Wake sleeping consumer */
if (s != 0)
errExitEN(s, "pthread_cond_signal");
Consumer thread:
for (;;) {
s = pthread_mutex_lock(&mtx);
if (s != 0)
errExitEN(s, "pthread_mutex_lock");
while (avail == 0) { /* Wait for something to consume */
s = pthread_cond_wait(&cond, &mtx);
if (s != 0)
errExitEN(s, "pthread_cond_wait");
}
while (avail > 0) { /* Consume all available units */
/* Do something with produced unit */
avail--;
}
s = pthread_mutex_unlock(&mtx);
if (s != 0)
errExitEN(s, "pthread_mutex_unlock");
/* Perhaps do other work here that doesn't require mutex lock */
}
Question: Why the first version is less effcient(wastes CPU time) compared to the second one? I can't see the difference.
Btw, could you give me an example which may illustrate the author's point of view, which I think is:
You can use mutex along with condition variable to improve perfomance, compared to the one that uses only mutex.
回答1:
To start off, both are problematic. You want the producer to produce items outside the lock, and use the lock only to enqueue the job and notify the consumer about it, and you weant the consumer to wait and dequeue inside the lock, but to "do something" with the job OUTSIDE of the lock.
Now, regarding the condition variable: A mutex allows you to synchronize the code of two or more threads to ensure that the code within the mutex runs with no interruptions in order to maintain data integrity: if 2 threads run i++ at the same time you have no guarantee that i will grow exactly by 2. So the mutex here solves the problem of enqueuing/dequeuing from one queue (or in this case the avail
variable) at the same time, but you do not have a mechanism to actually let the consumer sleep so not to waste precious CPU that can be used by the producer in order to produce more work. This is where the condition variable comes to help the consumer to sleep until the producers sets avail>0
and wakes the consumer up.
Example producer:
while (true) {
// produce outside the lock
...
pthread_mutex_lock(&mtx); // lock
++avail; // update new job
pthread_cond_signal(&cond); // wake up consumer
pthread_mutex_unlock(&mtx); // unlock
}
Example consumer:
while (true) {
pthread_mutex_lock(&mtx); // lock
while (avail<=0) // wait for new job
pthread_cond_wait(&cond, &mtx);
--avail; // get the new job
pthread_mutex_unlock(&mtx); // unlock
// consume job outside the lock
}
Note that I have removed error handling for simplification
For more reading
来源:https://stackoverflow.com/questions/59479143/why-this-example-using-mutex-is-less-efficient-compared-to-another-one-with-addi