Frage

Ich habe zu implementieren, die eine lese - /Schreibsperre in C++ unter Verwendung der Win32-api als Teil eines Projekts bei der Arbeit.Alle vorhandenen Lösungen verwenden von kernel-Objekten (Semaphoren und Mutexe), die erfordern, dass ein context-switch während der Ausführung.Das ist viel zu langsam für meine Anwendung.

Ich möchte implementieren, die nur mithilfe von kritischen Abschnitten, wenn möglich.Das Schloss muss nicht sein Prozess sicher, nur threadsicher.Irgendwelche Ideen auf, wie zu tun?

War es hilfreich?

Lösung

Ich glaube nicht, kann dies ohne Verwendung von mindestens ein Kernel-Level-Objekt (Mutex oder Semaphore) durchgeführt werden, da Sie die Hilfe des Kernels müssen die anrufProzessBlock zu machen, bis die Sperre verfügbar ist.

Kritische Abschnitte liefern blockiert, aber die API ist zu begrenzt. z.B. Sie können kein CS, entdecken, dass eine Lesesperre ist vorhanden, aber nicht eine Schreibsperre, und warten, bis der andere Prozess (weil zu Ende lesen greifen, wenn der andere Prozess den kritischen Abschnitt hat, wird es andere Leser blockieren, die falsch ist, und wenn es nicht dann nicht Ihr Prozess aber Spin blockieren, CPU-Zyklen zu brennen.)

Allerdings, was Sie tun können, ist ein Spin-Lock verwenden und fallen zurück auf eine Mutex, wenn es Streit ist. Der kritische Abschnitt wird sich auf diese Weise umgesetzt. Ich würde einen vorhandenen kritischen Abschnitt Durchführung und in das PID-Feld mit separaten Leser & Schreiber zählt ersetzen.

Andere Tipps

Wenn Sie Vista oder mehr ausrichten können, sollten Sie die eingebaute in SRWLock des . Sie sind wie kritische Abschnitte leicht, ganz im Benutzermodus, wenn es keine Konkurrenz ist.

Joe Duffy Blog hat einige aktuelle Einträge an der Umsetzung verschiedener Arten von non-blocking-Lese- / Schreibsperren. Diese Sperren tun Spin, so dass sie nicht angemessen wäre, wenn Sie beabsichtigen, eine Menge Arbeit zu tun, während die Sperre hält. Der Code ist C #, sollte aber in dem Hafen zu nativem unkompliziert sein.

Sie können einen Leser / Schreiber-Sperre mit kritischen Abschnitten und Ereignissen implementieren - Sie gerade genug Zustand nur das Signal der Veranstaltung halten müssen, wenn notwendig, einen unnötigen Kernel-Modus-Aufruf zu vermeiden

.

Alte Frage, aber das ist etwas, das sollte funktionieren. Es dreht sich nicht auf Streit. Leser entstehen begrenzte zusätzliche Kosten, wenn sie wenig oder gar keine Konkurrenz, weil SetEvent lazily genannt wird (Blick auf die Versionsgeschichte für eine Schwergewichts-Version, die nicht diese Optimierung hat).

#include <windows.h>

typedef struct _RW_LOCK {
    CRITICAL_SECTION countsLock;
    CRITICAL_SECTION writerLock;
    HANDLE noReaders;
    int readerCount;
    BOOL waitingWriter;
} RW_LOCK, *PRW_LOCK;

void rwlock_init(PRW_LOCK rwlock)
{
    InitializeCriticalSection(&rwlock->writerLock);
    InitializeCriticalSection(&rwlock->countsLock);

    /*
     * Could use a semaphore as well.  There can only be one waiter ever,
     * so I'm showing an auto-reset event here.
     */
    rwlock->noReaders = CreateEvent (NULL, FALSE, FALSE, NULL);
}

void rwlock_rdlock(PRW_LOCK rwlock)
{
    /*
     * We need to lock the writerLock too, otherwise a writer could
     * do the whole of rwlock_wrlock after the readerCount changed
     * from 0 to 1, but before the event was reset.
     */
    EnterCriticalSection(&rwlock->writerLock);
    EnterCriticalSection(&rwlock->countsLock);
    ++rwlock->readerCount;
    LeaveCriticalSection(&rwlock->countsLock);
    LeaveCriticalSection(&rwlock->writerLock);
}

int rwlock_wrlock(PRW_LOCK rwlock)
{
    EnterCriticalSection(&rwlock->writerLock);
    /*
     * readerCount cannot become non-zero within the writerLock CS,
     * but it can become zero...
     */
    if (rwlock->readerCount > 0) {
        EnterCriticalSection(&rwlock->countsLock);

        /* ... so test it again.  */
        if (rwlock->readerCount > 0) {
            rwlock->waitingWriter = TRUE;
            LeaveCriticalSection(&rwlock->countsLock);
            WaitForSingleObject(rwlock->noReaders, INFINITE);
        } else {
            /* How lucky, no need to wait.  */
            LeaveCriticalSection(&rwlock->countsLock);
        }
    }

    /* writerLock remains locked.  */
}

void rwlock_rdunlock(PRW_LOCK rwlock)
{
    EnterCriticalSection(&rwlock->countsLock);
    assert (rwlock->readerCount > 0);
    if (--rwlock->readerCount == 0) {
        if (rwlock->waitingWriter) {
            /*
             * Clear waitingWriter here to avoid taking countsLock
             * again in wrlock.
             */
            rwlock->waitingWriter = FALSE;
            SetEvent(rwlock->noReaders);
        }
    }
    LeaveCriticalSection(&rwlock->countsLock);
}

void rwlock_wrunlock(PRW_LOCK rwlock)
{
    LeaveCriticalSection(&rwlock->writerLock);
}

Sie könnten die Kosten für die Leser verringern, indem eine einzelne CRITICAL_SECTION mit:

  • countsLock mit writerLock in rdlock und rdunlock ersetzt

  • rwlock->waitingWriter = FALSE wird in wrunlock entfernt

  • wrlock Körper wird geändert

    EnterCriticalSection(&rwlock->writerLock);
    rwlock->waitingWriter = TRUE;
    while (rwlock->readerCount > 0) {
        LeaveCriticalSection(&rwlock->writerLock);
        WaitForSingleObject(rwlock->noReaders, INFINITE);
        EnterCriticalSection(&rwlock->writerLock);
    }
    rwlock->waitingWriter = FALSE;
    
    /* writerLock remains locked.  */
    

Allerdings verliert diese in Fairness, so dass ich es vorziehen, die obige Lösung.

Werfen Sie einen Blick auf das Buch " Concurrent Programming Windows " die hat viele verschiedene Referenzbeispiele für Leser / Schreiber-Sperren.

Überprüfen Sie heraus die spin_rw_mutex von Intel Thread Building Blocks ...

spin_rw_mutex streng in user-land und beschäftigt spin-warten Sie, bis die Blockierung

Dies ist eine alte Frage, aber vielleicht wird jemand diese nützlich finden. Wir entwickelten eine leistungsstarke, Open-Source-RWLock für Windows dass automatisch Vista + SRWLock verwendet Michael erwähnt falls vorhanden, oder sonst fällt zurück auf eine Userspace-Implementierung.

Als zusätzlichen Bonus gibt es vier unterschiedliche „Flavours“ davon (obwohl man auf die Grund haften können, die auch am schnellsten), die jeweils mehrere Synchronisationsoptionen. Es beginnt mit dem Grunde RWLock() die nichtablaufinvarianten ist, beschränkt auf Einzelprozesssynchronisation und ohne Austausch von Lese- / Schreibsperren zu einem vollwertigen prozessübergreifende IPC rwlock mit Wiedereintritt Unterstützung und Lese / Schreib-de-Erhebung.

Wie bereits erwähnt, sie tauschen sie dynamisch, um die Vista + slim-Lese-Schreibsperren für die beste Leistung, wenn möglich, aber Sie müssen nicht überhaupt Sorgen machen, da es auf Windows zurück auf eine voll kompatible Implementierung fällt XP und seine ilk.

Wenn Sie bereits von einer Lösung wissen, dass nur verwendet Mutexe, sollten Sie in der Lage sein, es zu ändern, anstatt kritische Abschnitte zu verwenden.

Wir rollten unsere eigenen mit zwei kritische Abschnitte und einige Zähler. Sie paßt unsere Bedürfnisse -. Wir eine sehr niedrigen Schriftsteller Anzahl haben, Schriftsteller Vorrang vor den Lesern, etc. Ich nicht frei bin unsrigen zu veröffentlichen, kann aber sagen, dass es ohne Mutexe und Semaphore möglich ist,

Hier ist die kleinste Lösung, die ich tun konnte mit:

http://www.baboonz.org/rwlock.php

Und eingefügt wörtlich:

/** A simple Reader/Writer Lock.

This RWL has no events - we rely solely on spinlocks and sleep() to yield control to other threads.
I don't know what the exact penalty is for using sleep vs events, but at least when there is no contention, we are basically
as fast as a critical section. This code is written for Windows, but it should be trivial to find the appropriate
equivalents on another OS.

**/
class TinyReaderWriterLock
{
public:
    volatile uint32 Main;
    static const uint32 WriteDesireBit = 0x80000000;

    void Noop( uint32 tick )
    {
        if ( ((tick + 1) & 0xfff) == 0 )     // Sleep after 4k cycles. Crude, but usually better than spinning indefinitely.
            Sleep(0);
    }

    TinyReaderWriterLock()                 { Main = 0; }
    ~TinyReaderWriterLock()                { ASSERT( Main == 0 ); }

    void EnterRead()
    {
        for ( uint32 tick = 0 ;; tick++ )
        {
            uint32 oldVal = Main;
            if ( (oldVal & WriteDesireBit) == 0 )
            {
                if ( InterlockedCompareExchange( (LONG*) &Main, oldVal + 1, oldVal ) == oldVal )
                    break;
            }
            Noop(tick);
        }
    }

    void EnterWrite()
    {
        for ( uint32 tick = 0 ;; tick++ )
        {
            if ( (tick & 0xfff) == 0 )                                     // Set the write-desire bit every 4k cycles (including cycle 0).
                _InterlockedOr( (LONG*) &Main, WriteDesireBit );

            uint32 oldVal = Main;
            if ( oldVal == WriteDesireBit )
            {
                if ( InterlockedCompareExchange( (LONG*) &Main, -1, WriteDesireBit ) == WriteDesireBit )
                    break;
            }
            Noop(tick);
        }
    }

    void LeaveRead()
    {
        ASSERT( Main != -1 );
        InterlockedDecrement( (LONG*) &Main );
    }
    void LeaveWrite()
    {
        ASSERT( Main == -1 );
        InterlockedIncrement( (LONG*) &Main );
    }
};

Sehen Sie meine Implementierung hier:

https://github.com/coolsoftware/LockLib

VRWLock ist eine C ++ Klasse, die einzelnen Schriftsteller implementiert -. Mehrere Leser Logik

Sehen Sie auch Testprojekt TestLock.sln.

UPD. Unten ist der einfache Code für Leser und Schreiber:

LONG gCounter = 0;

// reader

for (;;) //loop
{
  LONG n = InterlockedIncrement(&gCounter); 
  // n = value of gCounter after increment
  if (n <= MAX_READERS) break; // writer does not write anything - we can read
  InterlockedDecrement(&gCounter);
}
// read data here
InterlockedDecrement(&gCounter); // release reader

// writer

for (;;) //loop
{
  LONG n = InterlockedCompareExchange(&gCounter, (MAX_READERS+1), 0); 
  // n = value of gCounter before attempt to replace it by MAX_READERS+1 in InterlockedCompareExchange
  // if gCounter was 0 - no readers/writers and in gCounter will be MAX_READERS+1
  // if gCounter was not 0 - gCounter stays unchanged
  if (n == 0) break;
}
// write data here
InterlockedExchangeAdd(&gCounter, -(MAX_READERS+1)); // release writer

VRWLock Klasse unterstützt Spinzahl und fadenspezifischen Referenzzähler, die Sperren von terminierten Fäden freizugeben erlaubt.

Ich schrieb den folgenden Code nur kritische Abschnitte verwendet wird.

class ReadWriteLock {
    volatile LONG writelockcount;
    volatile LONG readlockcount;
    CRITICAL_SECTION cs;
public:
    ReadWriteLock() {
        InitializeCriticalSection(&cs);
        writelockcount = 0;
        readlockcount = 0;
    }
    ~ReadWriteLock() {
        DeleteCriticalSection(&cs);
    }
    void AcquireReaderLock() {        
    retry:
        while (writelockcount) {
            Sleep(0);
        }
        EnterCriticalSection(&cs);
        if (!writelockcount) {
            readlockcount++;
        }
        else {
            LeaveCriticalSection(&cs);
            goto retry;
        }
        LeaveCriticalSection(&cs);
    }
    void ReleaseReaderLock() {
        EnterCriticalSection(&cs);
        readlockcount--;
        LeaveCriticalSection(&cs);
    }
    void AcquireWriterLock() {
        retry:
        while (writelockcount||readlockcount) {
            Sleep(0);
        }
        EnterCriticalSection(&cs);
        if (!writelockcount&&!readlockcount) {
            writelockcount++;
        }
        else {
            LeaveCriticalSection(&cs);
            goto retry;
        }
        LeaveCriticalSection(&cs);
    }
    void ReleaseWriterLock() {
        EnterCriticalSection(&cs);
        writelockcount--;
        LeaveCriticalSection(&cs);
    }
};

einen Spin-wait auszuführen, kommentiert die Zeilen mit Sleep (0).

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top