信号量实现

发布于 2024-11-07 16:39:25 字数 344 浏览 0 评论 0原文

我想知道是否有一种方法可以在 C++(或 C#)中实现信号量,任何有帮助的库。我尝试使用 OpenMP,但我无法实际阻止线程,相反,我必须忙于等待它们,如果/当我没有足够数量的线程时,这会导致死锁。所以首先我正在寻找一个可以让我阻止/生成/杀死我的线程的库。
其次,是否有任何库已经实现了信号量?
最后,当我了解信号量的上下文时,我发现它非常有用(也许我错了?),但我没有看到很多库(如果有的话)实现它。我熟悉 OpenMP,查看过 Intel 的 TBB、C# 线程。但在这些中我都没有明确地看到信号量。那么信号量是不是没有我想象的那么实用呢?还是说它们很难实施?还是我没意识到?
附注
信号量可以跨平台实现吗?因为它们可能与操作系统有关。

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

御守 2024-11-14 16:39:26

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

此外,您还可以使用 Boost 实现信号量。检查这个出去。

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.

甜嗑 2024-11-14 16:39:26

第一个建议使用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;
};

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:

  • A count
  • A condition variable (that provides the suspend)
  • A mutex which provides the exclusiveness to modify the count and wait on the condition.

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;
};
赠意 2024-11-14 16:39:26

在 .NET 中,BCL 中存在一个实现: System.Threading .信号量

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

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).

蓝梦月影 2024-11-14 16:39:26

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

即使对于一个相当简单的生产者-消费者问题,基于信号量的解决方案是更难做对:进行信号量增量和信号量增量以错误的顺序递减可能会导致问题。相反,基于条件变量的解决方案不会出现这样的问题:条件变量与锁(互斥体)一起使用,并且自动施加正确的操作顺序;所以唤醒后,线程已经获得了锁。

另请参阅我对 何时应该使用信号量? 的回答,其中我给出了我认为条件变量的另一个例子更适合通常用信号量解决的问题。

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

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文