Using two semaphores to implement a bounded buffer has the advantage that writers and readers don't interfere with eachother until the queue is either empty or full. Of course, that might be the only good use of semaphores, and a direct implementation can be better then one using standard semaphores.
Semaphores can be implemented in a way that doesn't require a spinlock in the fast path. Still not as fast as you can make a mutex, but a lot better than the pseudocode given in many classes (which is almost always wrong, too.)