Семафорная реализация - PullRequest
2 голосов
/ 14 мая 2011

Мне было интересно, есть ли способ реализовать семафор в C ++ (или C #), какие-нибудь библиотеки, которые бы помогли.Я пытался использовать OpenMP, но у меня не было способа фактически блокировать потоки, вместо этого мне пришлось долго ждать их, что приводило к тупикам, если / когда у меня не было достаточного количества потоков.Итак, во-первых, я ищу какую-нибудь библиотеку, которая позволила бы мне блокировать / порождать / убивать мои потоки.
Во-вторых, существуют ли какие-либо библиотеки, которые уже реализуют семафоры?
И, наконец, когда я познакомился сконтекст семафоров Я нашел это очень полезным (может быть, я ошибаюсь?), но я не вижу много библиотек (если вообще), реализующих это.Я знаком с OpenMP, осматривал темы Intel TBB, C #.Но ни в одном из них я не вижу явно семафоров.Так неужели семафоры не такие практичные, как я думаю?Или их сложно реализовать?Или это я не в курсе?
PS
Могут ли семафоры быть реализованы кроссплатформенными?Поскольку они, вероятно, связаны с ОС.

Ответы [ 4 ]

6 голосов
/ 14 мая 2011

Существуют ли какие-либо библиотеки, которые уже реализуют это?
Для C ++ существует несколько многопоточных библиотек, которые предоставляют реализации семафоров:

Также Вы можете такжереализовать семафоры с помощью Boost.Проверьте это из.

3 голосов
/ 14 мая 2011

Первый совет, используйте повышение. Вся тяжелая работа выполнена.

Если вы хотите увидеть, как это реализовано, это должно выглядеть так (хотя это грубый набросок, я уверен, что некоторые исследования могут быть оптимизированы). В основном семафор построен из трех вещей:

  • Количество
  • Условная переменная (которая обеспечивает приостановку)
  • Мьютекс, который предоставляет исключительность для изменения счетчика и ожидания условия.

Вот простая версия:

#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 голосов
/ 14 мая 2011

В .NET существует реализация в BCL: System.Threading.Semaphore .

Для нативного кода в Windows взгляните на CreateSemaphore Function .Если вы ориентируетесь на Linux, вы можете найти семафорную реализацию Венского технического университета здесь (которую я уже использовал ранее и работает).

0 голосов
/ 15 мая 2011

В C ++ для блокировки потоков я бы рекомендовал вам использовать условные переменные, а не семафоры.В C # мониторы могут быть более подходящими.

Даже для довольно простого случая Проблема производителя-потребителя решение на основе семафора сложнее сделать правильно:выполнение приращений и уменьшений семафоров в неправильном порядке может привести к проблемам.Напротив, решение на основе условных переменных не будет иметь таких проблем: условные переменные используются с блокировкой (мьютексом), и правильный порядок операций устанавливается автоматически;поэтому после пробуждения поток уже получил блокировку.

См. также мой ответ на Когда мне следует использовать семафоры? , где я привожу другой пример условной переменной, которая, по моему мнению, более подходит дляпроблема, часто решаемая с семафорами.

И, чтобы ответить на другой ваш вопрос, я думаю, что более высокая вероятность ошибочного использования и более высокая сложность решений (по сравнению с альтернативами) являются причиной, по которой семафоры не предоставляются некоторыми потоками.Для TBB я могу сказать это наверняка.Поддержка потоков в C ++ 11 (разработанная после Boost.Thread) также не имеет ее;см. ответ Энтони Уильямса почему.

...