2 Stimmen

Semaphor-Implementierung

Ich fragte mich, wenn es eine Möglichkeit, Semaphore in C++ (oder C#), alle Bibliotheken, die helfen würde zu implementieren. Ich habe versucht, mit OpenMP, aber ich hatte keine Möglichkeit, tatsächlich blockieren Threads, stattdessen musste ich beschäftigt warten auf sie, die zu Deadlocks führen, wenn ich nicht genug Anzahl von Threads hatte. Ich suche also zunächst nach einer Bibliothek, mit der ich meine Threads blockieren/spawnen/beenden kann.
Zweitens, gibt es irgendwelche Bibliotheken da draußen, die bereits Semaphoren implementieren?
Und schließlich, als ich in den Kontext von Semaphoren eingeführt wurde, fand ich sie sehr nützlich (vielleicht liege ich falsch?), aber ich sehe nicht viele Bibliotheken (wenn überhaupt), die sie implementieren. Ich bin mit OpenMP vertraut, habe mich in Intels TBB und C# Threads umgesehen. Aber in keiner dieser Bibliotheken sehe ich explizit Semaphoren. Sind Semaphoren also nicht so praktisch, wie ich denke? Oder ist es so, dass sie schwer zu implementieren sind? Oder liegt es daran, dass ich es nicht weiß?
P.S.
Können Semaphoren plattformübergreifend implementiert werden? Da sie wahrscheinlich mit dem Betriebssystem zusammenhängen.

6voto

Alok Save Punkte 196241

Gibt es Bibliotheken, die dies bereits umsetzen?
Für C++ gibt es mehrere Multithreading-Bibliotheken, die Semaphore-Implementierungen anbieten:

Außerdem können Sie Semaphoren auch mit Boost implementieren. Prüfen Sie este aus.

3voto

Martin York Punkte 245363

Erster Rat: Verwenden Sie Boost. Die ganze harte Arbeit ist bereits getan.

Wenn Sie sehen wollen, wie es implementiert wird, sollte es so aussehen (obwohl dies eine grobe Skizze ist, bin ich sicher, dass sie mit etwas Forschung optimiert werden kann). Grundsätzlich wird ein Semaphor aus drei Dingen aufgebaut:

  • Ein Graf
  • Eine Bedingungsvariable (die die Aussetzung bewirkt)
  • Ein Mutex, der die Exklusivität bietet, den Zählerstand zu ändern und auf die Bedingung zu warten.

Hier ist die einfache 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;
};

2voto

Oliver Hanappi Punkte 11642

In .NET gibt es eine Implementierung innerhalb der BCL: System.Threading.Semaphore .

Für nativen Code unter Windows, schauen Sie sich die Funktion CreateSemaphore . Wenn Sie auf Linux abzielen, dann finden Sie eine Semaphor-Implementierung der Technischen Universität Wien aquí (die ich bereits früher verwendet habe und die funktioniert).

0voto

Alexey Kukanov Punkte 12021

In C++ empfehle ich Ihnen für die Blockierung von Threads die Verwendung von Zustandsvariablen anstelle von Semaphore. In C#, überwacht angemessener sein könnte.

Selbst für einen recht einfachen Fall von Erzeuger-Verbraucher-Problem Eine Semaphor-basierte Lösung ist schwieriger zu bewerkstelligen: Semaphor-Inkremente und -Dekremente in einer falschen Reihenfolge könnten zu Problemen führen. Bei einer Lösung auf der Grundlage von Zustandsvariablen gibt es dagegen keine derartigen Probleme: Zustandsvariablen werden mit einer Sperre (Mutex) verwendet, und die richtige Reihenfolge der Operationen wird automatisch festgelegt; so hat ein Thread nach dem Aufwachen bereits die Sperre erworben.

Siehe auch meine Antwort auf Wann sollte ich Semaphoren verwenden? wo ich ein weiteres Beispiel für eine Bedingungsvariable anführe, die meiner Meinung nach besser für ein Problem geeignet ist, das häufig mit Semaphoren gelöst wird.

Und um eine weitere Frage zu beantworten: Ich denke, dass die höhere Anfälligkeit für Fehler und die höhere Komplexität der Lösungen (im Vergleich zu Alternativen) der Grund dafür sind, dass Semaphoren nicht von einigen Threading-Paketen bereitgestellt werden. Für TBB kann ich das mit Sicherheit sagen. Die Thread-Unterstützung in C++11 (die nach Boost.Thread entwickelt wurde) bietet sie ebenfalls nicht; siehe Antwort von Anthony Williams warum.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X