Reputation: 7672
I was wondering if there was a way to implement semaphore in C++ (or C#), any libraries that'd help. I tried using OpenMP but I had no way of actually blocking threads, instead I had to busy wait on 'em which lead to deadlocks if/when I hadn't enough number of threads. So First I'm looking for a some library that would let me block/spawn/kill my threads.
Secondly, are there any libraries out there that already implement semaphores?
And finally, when I was introduced to the context of semaphores I found it very useful (maybe I'm wrong?) but I don't see many libraries (if at all) implementing it. I'm familiar with OpenMP, looked around Intel's TBB, C# threads. But in none of these I don't see semaphores explicitly. So are semaphores not as practical as I think? Or is it that they're hard to implement? Or is it me not being aware?
P.S.
Can semaphores be implemented cross-platform? Since they're probably related to OS.
Upvotes: 2
Views: 4720
Reputation: 264331
First advice use boost. All the hard work has been done.
If you want to see how it is implemented it should look like this (though this is a rough sketch I am sure with some research ti can be optimized). Basically a semaphore is built from three things:
Here is the simple version:
#include <pthread.h>
// Need an exception safe locking class.
struct MutexLocker
{
MutexLocker(pthread_mutex_t& m) :mutex(m)
{ if (pthread_mutex_lock(&mutex) != 0) {throw int(1); }}
~MutexLocker()
{ if (pthread_mutex_unlock(&mutex) != 0) {throw int(1); }}
private:
pthread_mutex_t& mutex;
};
class Semaphore
{
public:
Semaphore(int initCount = 0)
: count(initCount)
, waitCount(0)
{
if (pthread_mutex_init(&mutex, NULL) != 0)
{ throw int(1);
}
if (pthread_cond_init(&cond, NULL) != 0)
{ pthread_mutex_destroy(&mutex);
throw int(2);
}
}
void wait()
{
MutexLocker locker(mutex);
while(count == 0)
{
++waitCount;
if (pthread_cond_wait(&cond, &mutex) != 0)
{ throw int(2);
}
// A call to pthread_cond_wait() unlocks the mutex and suspends the thread.
// It does not busy wait the thread is suspended.
//
// When a condition variable receives apthread_cond_signal() a random thread
// is un-suspended. But it is not released from the call to wait
// until the mutex can be reacquired by the thread.
//
// Thus we get here only after the mutex has been locked.
//
// You need to use a while loop above because of this potential situation.
// Thread A: Suspended waiting on condition variable.
// Thread B: Working somewhere else.
// Thread C: calls signal() below (incrementing count to 1)
// This results in A being awakened but it can not exit pthread_cond_wait()
// until it requires the mutex with a lock. While it tries to
// do that thread B finishes what it was doing and calls wait()
// Thread C has incremented the count to 1 so thread B does not
// suspend but decrements the count to zero and exits.
// Thread B now aquires the mutex but the count has been decremented to
// zero so it must immediately re-suspend on the condition variable.
// Note a thread will not be released from wait until
// it receives a signal and the mustex lock can be re-established.
--waitCount;
}
--count;
}
void signal()
{
// You could optimize this part with interlocked increment.
MutexLocker locker(mutex);
++count;
// This Comment based on using `interlocked increment` rather than mutex.
//
// As this part does not modify anything you don;t actually need the lock.
// Potentially this will release more threads than you need (as you don't
// have exclusivity on reading waitCount but that will not matter as the
// wait() method does and any extra woken threads will be put back to sleep.
// If there are any waiting threads let them out.
if (waitCount > 0)
{ if (pthread_cond_signal(&cond) != 0)
{ throw int(2);
}
}
}
private:
unsigned int count;
unsigned int waitCount;
pthread_mutex_t mutex;
pthread_cond_t cond;
};
Upvotes: 4
Reputation: 12774
In C++, for the way to block threads I would recommend you to use condition variables rather than semaphores. In C#, monitors might be more appropriate.
Even for a rather simple case of Producer-Consumer problem, a semaphore-based solution is harder to do right: doing semaphore increments & decrements in a wrong order could result in problems. On the contrary, a condition-variable-based solution would not have such issues: condition variables are used with a lock (mutex) and the right order of operations is imposed automatically; so after wakeup, a thread already has the lock acquired.
See also my asnwer to When should I use semaphores? where I give another example of condition variable being in my opinion more appropriate for a problem often solved with semaphores.
And to address another your question, I think higher liability to erroneous use and higher complexity of solutions (comparing to alternatives) are the reason why semaphores are not provided by some threading packages. For TBB, I can say that for sure. Thread support in C++11 (designed after Boost.Thread) does not have it too; see Anthony Williams' answer why.
Upvotes: 0
Reputation: 206508
Are there any libraries out there that already implement this?
For C++ there are multiple multithreading libraries, which provide Semaphore implementations:
Also, You can also implement Semaphores using Boost. Check this out.
Upvotes: 6
Reputation: 12336
In .NET, there exists an implementation within the BCL: System.Threading.Semaphore.
For native code on Windows, have a look at the CreateSemaphore Function. If you are targeting Linux, then you can find a semaphore implementation of the Vienna University of Technology here (which I have already used before and works).
Upvotes: 2