سؤال

لدي لتنفيذ قراءة/كتابة قفل في C++ باستخدام Win32 api كجزء من مشروع في العمل.جميع الحلول القائمة استخدام نواة الكائنات (الإشارات و mutexes) التي تتطلب تبديل السياق أثناء التنفيذ.هذه هي بطيئة جدا عن طلبي.

أود تنفيذ واحدة فقط باستخدام المقاطع الحرجة ، إذا كان ذلك ممكنا.قفل لا يجب أن تكون عملية آمنة فقط threadsafe.أي أفكار حول كيفية التعامل مع هذا ؟

هل كانت مفيدة؟

المحلول

وأنا لا أعتقد أن هذا يمكن أن يتم دون استخدام الكائن على مستوى النواة واحد على الأقل (أو المزامنة سيمافور)، لأنك بحاجة إلى مساعدة من نواة لجعل كتلة عملية الاستدعاء حتى يتم قفل المتاحة.

والأقسام الحرجة لا توفر عرقلة، ولكن API محدودة جدا. مثلا لا يمكنك انتزاع CS، يكتشف أن تأمين قراءة متاح ولكن ليس تأمين كتابة، والانتظار لعملية أخرى لإنهاء القراءة (لأنه إذا كان عملية أخرى لديها مقطع حرج فإنه سيتم منع القراء الآخرين وهذا خطأ، وإذا كان لا ثم سوف عملية الخاص بك لا يمنع ولكن تدور، وحرق دورات CPU).

ولكن ما يمكنك القيام به هو استخدام تأمين زيادة ونقصان وتراجع إلى مزامنة كلما كان هناك خلاف. مقطع حرج هو في حد ذاته تنفذ بهذه الطريقة. وأود أن أنتهز التنفيذ مقطع حرج الحالية واستبدال الحقل PID مع القارئ والكاتب تهم منفصلة.

نصائح أخرى

إذا يمكنك استهداف فيستا أو أكثر، يجب عليك استخدام المدمج في <لأ href = "http://msdn.microsoft.com/en-us/library/aa904937(VS.85).aspx" يختلط = "noreferrer"> SRWLock في . وخفيفة كانوا مثل مقاطع الهامة، تماما وضع المستخدم عندما لا يكون هناك خلاف.

بلوق

وجو دافي لديه بعض إدخالات على تنفيذ مختلف أنواع عدم حجب أقفال القارئ / الكاتب. هذه الأقفال تفعل تدور، لذلك فإنها لن يكون من المناسب إذا كنت تنوي القيام بالكثير من العمل في حين عقد التأمين. هو رمز C #، ولكن ينبغي أن تكون صريحة إلى الميناء الأم.

ويمكنك تنفيذ قفل القارئ / الكاتب باستخدام الأقسام والأحداث الحرجة - كل ما تحتاجه للحفاظ على الدولة ما يكفي فقط لإشارة الحدث عند الضرورة لتجنب مكالمة وضع kernel غير الضرورية

.

السؤال القديم, ولكن هذا هو الشيء الذي يجب أن تعمل.لا تدور على الخلاف.القراء تحمل محدودة تكلفة إضافية إذا كان لديهم القليل أو لا الخلاف ، لأن SetEvent ويسمى بتكاسل (انظر تاريخ تحرير أكثر من الوزن الثقيل النسخة التي لا يكون هذا التحسين).

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

يمكنك خفض تكلفة للقراء باستخدام واحد CRITICAL_SECTION:

  • countsLock يتم استبدال writerLock في rdlock و rdunlock

  • rwlock->waitingWriter = FALSE تتم إزالة في wrunlock

  • wrlock الجسم هو تغيير

    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.  */
    

ولكن هذا يفقد في الإنصاف ، لذلك أنا أفضل حل أعلاه.

ونلقي نظرة على كتاب " البرمجة المتزامنة على ويندوز " التي لديها الكثير من الأمثلة مرجعية مختلفة لأقفال القارئ / الكاتب.

وراجع في spin_rw_mutex من rel="nofollow الموضوع بناء كتل ...

<اقتباس فقرة>   

وspin_rw_mutex بشكل صارم في المستخدم الأراضي   ويعمل تدور الانتظار لحجب

وهذا هو السؤال القديم ولكن ربما شخص ما سوف تجد هذا مفيدا. قمنا بتطوير عالية الأداء، RWLock المصدر المفتوح ويندوز يستخدم تلقائيا فيستا + SRWLock <أ href = ل "https://stackoverflow.com/a/1008891/17027">Michael المذكورة إن وجدت، أو يرتد إلى تطبيق مساحة المستخدم.

واضاف باعتبارها مكافأة، وهناك أربع "النكهات" مختلفة منه (على الرغم من أنك يمكن التمسك الأساسية، وهي أيضا أسرع)، ولكل وتوفير المزيد من خيارات المزامنة. ويبدأ مع RWLock() الأساسية وهي عدم عودة الدخول، تقتصر على تزامن عملية واحدة، وليس مبادلة أقفال القراءة / الكتابة إلى عبر عملية كاملة IPC RWLock بدعم إعادة مدخل والقراءة / الكتابة دي الارتفاع.

وكما ذكر، وتبادل حيوي إلى ويندوز فيستا + ضئيلة أقفال للقراءة والكتابة للحصول على أفضل أداء عندما يكون ذلك ممكنا، ولكن لم يكن لديك ما يدعو للقلق حول ذلك على الإطلاق لأنها سوف تقع مرة أخرى إلى التنفيذ متوافقة تماما على ويندوز XP وأمثالها.

إذا كنت تعرف بالفعل من حل فقط يستخدم mutexes ، يجب أن تكون قادرا على تعديل ذلك إلى استخدام المقاطع الحرجة بدلا من ذلك.

لقد توالت الخاصة بنا باستخدام اثنين من المقاطع الحرجة وبعض العدادات.أنها تناسب احتياجاتنا لدينا منخفضة جدا الكاتب العد والكتاب الحصول على الأسبقية على القراء ، إلخ.أنا لا الحرية في نشر لنا ولكن يمكن القول أنه من الممكن دون mutexes و الإشارات.

وهنا هو أصغر الحل أن أتمكن من الخروج مع:

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

ولصق حرفيا:

/** 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 );
    }
};

وانظروا تنفيذ بلدي هنا:

https://github.com/coolsoftware/LockLib

وVRWLock هي فئة C ++ التي تطبق الكاتب وحيد - متعددة القراء منطق

وبحث أيضا TestLock.sln مشروع اختبار.

وUPD. وفيما يلي رمز بسيط للقارئ والكاتب:

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 تدعم عدد تدور وموضوع محدد عدد مرجع التي تسمح للافراج عن خصلات المواضيع إنهاؤها.

وكتبت التعليمة البرمجية التالية باستخدام مقاطع الهامة فقط.

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

لإجراء تدور الانتظار للتعليق سطور مع النوم (0).

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top