信号量实施

时间:2011-05-14 08:31:23

标签: c# c++ implementation semaphore

我想知道是否有办法在C ++(或C#)中实现信号量,任何有帮助的库。我尝试使用OpenMP,但是我无法实际阻塞线程,而是我不得不忙着等待,如果/当我没有足够数量的线程时会导致死锁。首先,我正在寻找一个可以让我阻止/生成/杀死我的线程的库 其次,有没有已经实现信号量的图书馆? 最后,当我被介绍到信号量的上下文时,我发现它非常有用(也许我错了?)但我没有看到很多库(如果有的话)实现它。我熟悉OpenMP,环顾英特尔的TBB,C#线程。但是在这些中我都没有明确地看到信号量。那些信号量不像我想的那么实用吗?还是他们难以实施?还是我不知道? 附:
信号量可以跨平台实现吗?因为它们可能与操作系统有关。

4 个答案:

答案 0 :(得分:6)

是否有任何库已经实现了这个?
对于C ++,有多个多线程库,它们提供信号量实现:

此外,您还可以使用Boost实现信号量。检查this

答案 1 :(得分:3)

首先建议使用boost。所有艰苦的工作都已完成。

如果你想看看它是如何实现的,它应该是这样的(尽管这是一个粗略的草图我相信一些研究可以优化)。基本上信号量是由三件事构成的:

  • 计数
  • 条件变量(提供暂停)
  • 一个互斥体,提供修改计数和等待条件的排他性。

以下是简单版本:

#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;
};

答案 2 :(得分:2)

在.NET中,BCL中存在一个实现:System.Threading.Semaphore

对于Windows上的本机代码,请查看CreateSemaphore Function。如果您的目标是Linux,那么您可以找到维也纳科技大学here的信号量实现(我之前已经使用过它并且可以使用)。

答案 3 :(得分:0)

在C ++中,对于阻塞线程的方法,我建议你使用条件变量而不是信号量。在C#中,monitors可能更合适。

即使对于Producer-Consumer problem的一个相当简单的情况,基于信号量的解决方案也很难做到:做信号量增量&amp;错误顺序的减量可能会导致问题。相反,基于条件变量的解决方案不会出现这样的问题:条件变量与锁(互斥锁)一起使用,并且自动施加正确的操作顺序;所以在唤醒之后,一个线程已经获得了锁。

另请参阅我的asnwer When should I use semaphores?其中我给出了另一个条件变量的例子,在我看来更适合于通常用信号量解决的问题。

为了解决另一个问题,我认为错误使用的更高责任和更高的解决方案复杂性(与替代方案相比)是某些线程包不提供信号量的原因。对于TBB,我可以肯定地说。 C ++ 11中的线程支持(在Boost.Thread之后设计)也没有它;见Anthony Williams' answer为什么。