pthread_cond_wait versus semaphore

2019-01-20 23:01发布

What are the pros / cons of using pthread_cond_wait or using a semaphore ? I am waiting for a state change like this :

pthread_mutex_lock(&cam->video_lock);
while(cam->status == WAIT_DISPLAY) {
    pthread_cond_wait(&cam->video_cond, &cam->video_lock);
}
pthread_mutex_unlock(&cam->video_lock);

Using a properly initialised semaphore, I think I could do it like this :

while(cam->status == WAIT_DISPLAY) {
    sem_wait(&some_semaphore);
}

What are the pros and cons of each method ?

4条回答
▲ chillily
2楼-- · 2019-01-20 23:21

A semaphore is suited cleanly to a producer-consumer model, although it has other uses. Your program logic is responsible for ensuring that the right number of posts are made for the number of waits. If you post a semaphore and nobody is waiting on it yet, then when they do wait they continue immediately. If your problem is such that it can be explained in terms of the count value of a semaphore, then it should be easy to solve with a semaphore.

A condition variable is a bit more forgiving in some respects. You can for example use cond_broadcast to wake up all waiters, without the producer knowing how many there are. And if you cond_signal a condvar with nobody waiting on it then nothing happens. This is good if you don't know whether there's going to be a listener interested. It is also why the listener should always check the state with the mutex held before waiting - if they don't then they can miss a signal and not wake up until the next one (which could be never).

So a condition variable is suitable for notifying interested parties that state has changed: you acquire the mutex, change the state, signal (or broadcast) the condvar and release the mutex. If this describes your problem you're in condvar territory. If different listeners are interested in different states you can just broadcast and they'll each in turn wake up, figure out whether they've found the state they want, and if not wait again.

It's very gnarly indeed to attempt this sort of thing with a mutex and a semaphore. The problem comes when you want to take the mutex, check some state, then wait on the semaphore for changes. Unless you can atomically release the mutex and wait on the semaphore (which in pthreads you can't), you end up waiting on the semaphore while holding the mutex. This blocks the mutex, meaning that others can't take it to make the change you care about. So you will be tempted to add another mutex in a way which depends on your specific requirements. And maybe another semaphore. The result is generally incorrect code with harmful race conditions.

Condition variables escape this problem, because calling cond_wait automatically releases the mutex, freeing it for use by others. The mutex is regained before cond_wait returns.

IIRC it is possible to implement a kind of condvar using only semaphores, but if the mutex you're implementing to go with the condvar is required to have trylock, then it's a serious head-scratcher, and timed waits are out. Not recommended. So don't assume that anything you can do with a condvar can be done with semaphores. Plus of course mutexes can have nice behaviours that semaphores lack, principally priority-inversion avoidance.

查看更多
你好瞎i
3楼-- · 2019-01-20 23:22

The 2nd snippet is racy, don't do that.

The other answers have a nice discussion of the relative merits; I'll just add that pthread_cond_broadcast is a clear advantage of condition variables.

Beyond that, I'm just more used to condition variables for that, as they are what you use in Java, even because they help you to avoid races when checking the shared flags.

Indeed, in the 2nd snippet you don't have any lock protecting the read of cam->status, so it is accessed through a data race. Most platforms will let you get away with that in this particular example, but that has undefined semantics, by POSIX and by the memory model of the next C/C++ standards.

In fact, a real race condition is possible if another thread allocates a new cam structure and overwrites cam; the waiting thread might see the update to the 'cam' pointer without seeing the initialization of cam->status. Indeed, the 2nd snippet is asking for trouble, in this case and in general.

http://www.hpl.hp.com/personal/Hans_Boehm/c++mm/

查看更多
趁早两清
4楼-- · 2019-01-20 23:33

In your second snippet, you're getting the lock multitude of times, never releasing it.

In general, the state you're waintin on can be completely expressed by a semaphore, then you can use just that. A lock structure is smaller in size, and it requires less atomic operations to check/set/release.

Otherwise, if the state is complex, and different parts of the code wait on different conditions of the same variable (eg, here you want x<10; there you want y>x), use cond_wait.

查看更多
贪生不怕死
5楼-- · 2019-01-20 23:35

Conditionals let you do some things that semaphores won't.

For example, suppose you have some code which requires a mutex, called m. It however needs to wait until some other thread has finish their task, so it waits on a semaphore called s. Now any thread which needs m is blocked from running, even though the thread which has m is waiting on s. These kind of situations can be resolved using conditionals. When you wait on a conditional, the mutex currently held is released, so other threads can acquire the mutex. So back to our example, and suppose conditional c was used instead of s. Our thread now acquires m, and then conditional waits on c. This releases m so other threads can proceed. When c becomes available, m is reacquired, and our original thread can continue merrily along its way.

Conditional variables also allows you to let all threads waiting on a conditional variable to proceed via pthread_cond_broadcast. Additionally it also allows you to perform a timed wait so you don't end up waiting forever.

Of course, sometimes you don't need conditional variables, so depending on your requirements, one or the other may be better.

查看更多
登录 后发表回答